Skip to content

Commit

Permalink
[Lens] Edit data view in flyout (elastic#142362)
Browse files Browse the repository at this point in the history
* add explore matching index

* adjust type

* move things around

* fix types

* fix tests

* fix imports

* fix limit

* do not clean datasource on adding ad hoc data view

* manage data view in flyout

* fix phrase

* make sure all changes are propagated correctly

* fix test

* Update src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx

Co-authored-by: Anton Dosov <[email protected]>

* only show for persisted data views

Co-authored-by: Stratoula Kalafateli <[email protected]>
Co-authored-by: Anton Dosov <[email protected]>
  • Loading branch information
3 people authored and WafaaNasr committed Oct 11, 2022
1 parent 767bcef commit 282b7c8
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
*/

import React, { useState, useEffect, useCallback, useRef } from 'react';
import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
import {
EuiTitle,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiLoadingSpinner,
EuiLink,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import memoizeOne from 'memoize-one';
import {
Expand Down Expand Up @@ -67,6 +74,7 @@ export interface Props {
defaultTypeIsRollup?: boolean;
requireTimestampField?: boolean;
editData?: DataView;
showManagementLink?: boolean;
allowAdHoc: boolean;
}

Expand All @@ -85,9 +93,10 @@ const IndexPatternEditorFlyoutContentComponent = ({
requireTimestampField = false,
editData,
allowAdHoc,
showManagementLink,
}: Props) => {
const {
services: { http, dataViews, uiSettings, overlays },
services: { application, http, dataViews, uiSettings, overlays },
} = useKibana<DataViewEditorContext>();

const { form } = useForm<IndexPatternConfig, FormInternal>({
Expand Down Expand Up @@ -376,6 +385,17 @@ const IndexPatternEditorFlyoutContentComponent = ({
<EuiTitle data-test-subj="flyoutTitle">
<h2>{editData ? editorTitleEditMode : editorTitle}</h2>
</EuiTitle>
{showManagementLink && editData && editData.id && (
<EuiLink
href={application.getUrlForApp('management', {
path: `/kibana/dataViews/dataView/${editData.id}`,
})}
>
{i18n.translate('indexPatternEditor.goToManagementPage', {
defaultMessage: 'View on data view management page',
})}
</EuiLink>
)}
<Form form={form} className="indexPatternEditor__form">
<UseField path="isAdHoc" />
{indexPatternTypeSelect}
Expand Down Expand Up @@ -425,6 +445,7 @@ const IndexPatternEditorFlyoutContentComponent = ({
}}
submitDisabled={form.isSubmitted && !form.isValid}
isEdit={!!editData}
isPersisted={Boolean(editData && editData.isPersisted())}
allowAdHoc={allowAdHoc}
/>
</FlyoutPanels.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const IndexPatternFlyoutContentContainer = ({
requireTimestampField = false,
editData,
allowAdHocDataView,
showManagementLink,
}: DataViewEditorProps) => {
const {
services: { dataViews, notifications },
Expand Down Expand Up @@ -68,6 +69,7 @@ const IndexPatternFlyoutContentContainer = ({
defaultTypeIsRollup={defaultTypeIsRollup}
requireTimestampField={requireTimestampField}
editData={editData}
showManagementLink={showManagementLink}
allowAdHoc={allowAdHocDataView || false}
/>
);
Expand Down
23 changes: 21 additions & 2 deletions src/plugins/data_view_editor/public/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface FooterProps {
onSubmit: (isAdHoc?: boolean) => void;
submitDisabled: boolean;
isEdit: boolean;
isPersisted: boolean;
allowAdHoc: boolean;
}

Expand All @@ -37,11 +38,25 @@ const editButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutEditButt
defaultMessage: 'Save',
});

const editUnpersistedButtonLabel = i18n.translate(
'indexPatternEditor.editor.flyoutEditUnpersistedButtonLabel',
{
defaultMessage: 'Continue to use without saving',
}
);

const exploreButtonLabel = i18n.translate('indexPatternEditor.editor.flyoutExploreButtonLabel', {
defaultMessage: 'Use without saving',
});

export const Footer = ({ onCancel, onSubmit, submitDisabled, isEdit, allowAdHoc }: FooterProps) => {
export const Footer = ({
onCancel,
onSubmit,
submitDisabled,
isEdit,
allowAdHoc,
isPersisted,
}: FooterProps) => {
const submitPersisted = () => {
onSubmit(false);
};
Expand Down Expand Up @@ -89,7 +104,11 @@ export const Footer = ({ onCancel, onSubmit, submitDisabled, isEdit, allowAdHoc
fill
disabled={submitDisabled}
>
{isEdit ? editButtonLabel : saveButtonLabel}
{isEdit
? isPersisted
? editButtonLabel
: editUnpersistedButtonLabel
: saveButtonLabel}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/data_view_editor/public/open_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const getEditorOpener =
notifications,
application,
dataViews,
overlays,
searchClient,
});

Expand All @@ -46,6 +47,7 @@ export const getEditorOpener =
defaultTypeIsRollup = false,
requireTimestampField = false,
allowAdHocDataView = false,
editData,
}: DataViewEditorProps): CloseEditor => {
const closeEditor = () => {
if (overlayRef) {
Expand All @@ -72,9 +74,11 @@ export const getEditorOpener =
closeEditor();
onCancel();
}}
editData={editData}
defaultTypeIsRollup={defaultTypeIsRollup}
requireTimestampField={requireTimestampField}
allowAdHocDataView={allowAdHocDataView}
showManagementLink={Boolean(editData && editData.isPersisted())}
/>
</I18nProvider>
</KibanaReactContextProvider>,
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/data_view_editor/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class DataViewEditorPlugin
}

public start(core: CoreStart, plugins: StartPlugins) {
const { application, uiSettings, docLinks, http, notifications } = core;
const { application, uiSettings, docLinks, http, notifications, overlays } = core;
const { data, dataViews } = plugins;

return {
Expand All @@ -48,6 +48,7 @@ export class DataViewEditorPlugin
http,
notifications,
application,
overlays,
dataViews,
searchClient: data.search.search,
}}
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/data_view_editor/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
NotificationsStart,
DocLinksStart,
HttpSetup,
OverlayStart,
} from '@kbn/core/public';

import { EuiComboBoxOptionOption } from '@elastic/eui';
Expand All @@ -31,6 +32,7 @@ export interface DataViewEditorContext {
http: HttpSetup;
notifications: NotificationsStart;
application: ApplicationStart;
overlays: OverlayStart;
dataViews: DataViewsPublicPluginStart;
searchClient: DataPublicPluginStart['search']['search'];
}
Expand Down Expand Up @@ -62,6 +64,11 @@ export interface DataViewEditorProps {
* if set to true user is presented with an option to create ad-hoc dataview without a saved object.
*/
allowAdHocDataView?: boolean;

/**
* if set to true a link to the management page is shown
*/
showManagementLink?: boolean;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export function ChangeDataView({
onTextLangQuerySubmit,
textBasedLanguage,
isDisabled,
onEditDataView,
onCreateDefaultAdHocDataView,
}: DataViewPickerPropsExtended) {
const { euiTheme } = useEuiTheme();
Expand All @@ -88,7 +89,7 @@ export function ChangeDataView({
const [selectedDataViewId, setSelectedDataViewId] = useState(currentDataViewId);

const kibana = useKibana<IUnifiedSearchPluginServices>();
const { application, data, storage } = kibana.services;
const { application, data, storage, dataViews, dataViewEditor } = kibana.services;
const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth });
const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() =>
Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY))
Expand Down Expand Up @@ -193,11 +194,21 @@ export function ChangeDataView({
key="manage"
icon="indexSettings"
data-test-subj="indexPattern-manage-field"
onClick={() => {
onClick={async () => {
if (onEditDataView) {
const dataView = await dataViews.get(currentDataViewId!);
dataViewEditor.openEditor({
editData: dataView,
onSave: (updatedDataView) => {
onEditDataView(updatedDataView);
},
});
} else {
application.navigateToApp('management', {
path: `/kibana/indexPatterns/patterns/${currentDataViewId}`,
});
}
setPopoverIsOpen(false);
application.navigateToApp('management', {
path: `/kibana/indexPatterns/patterns/${currentDataViewId}`,
});
}}
>
{i18n.translate('unifiedSearch.query.queryBar.indexPattern.manageFieldButton', {
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/unified_search/public/dataview_picker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export interface DataViewPickerProps {
* Callback that is called when the user changes the currently selected dataview.
*/
onChangeDataView: (newId: string) => void;
/**
* Callback that is called when the user edits the current data view via flyout.
* The first parameter is the updated data view stub without fetched fields
*/
onEditDataView?: (updatedDataViewStub: DataView) => void;
/**
* The id of the selected dataview.
*/
Expand Down Expand Up @@ -98,6 +103,7 @@ export const DataViewPicker = ({
currentDataViewId,
adHocDataViews,
onChangeDataView,
onEditDataView,
onAddField,
onDataViewCreated,
trigger,
Expand All @@ -114,6 +120,7 @@ export const DataViewPicker = ({
isMissingCurrent={isMissingCurrent}
currentDataViewId={currentDataViewId}
onChangeDataView={onChangeDataView}
onEditDataView={onEditDataView}
onAddField={onAddField}
onDataViewCreated={onDataViewCreated}
onCreateDefaultAdHocDataView={onCreateDefaultAdHocDataView}
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/unified_search/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
Expand Down Expand Up @@ -87,5 +88,6 @@ export interface IUnifiedSearchPluginServices extends Partial<CoreStart> {
docLinks: DocLinksStart;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
dataViewEditor: DataViewEditorStart;
usageCollection?: UsageCollectionStart;
}
69 changes: 45 additions & 24 deletions x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ export const LensTopNavMenu = ({
[dispatch]
);
const [indexPatterns, setIndexPatterns] = useState<DataView[]>([]);
const [dataViewsList, setDataViewsList] = useState<DataView[]>([]);
const [currentIndexPattern, setCurrentIndexPattern] = useState<DataView>();
const [isOnTextBasedMode, setIsOnTextBasedMode] = useState(false);
const [rejectedIndexPatterns, setRejectedIndexPatterns] = useState<string[]>([]);
Expand Down Expand Up @@ -357,27 +356,18 @@ export const LensTopNavMenu = ({
]);

useEffect(() => {
if (activeDatasourceId && datasourceStates[activeDatasourceId].state) {
const dataViewId = datasourceMap[activeDatasourceId].getUsedDataView(
datasourceStates[activeDatasourceId].state
);
const dataView = dataViewsList.find((pattern) => pattern.id === dataViewId);
setCurrentIndexPattern(dataView ?? indexPatterns[0]);
}
}, [activeDatasourceId, datasourceMap, datasourceStates, indexPatterns, dataViewsList]);

useEffect(() => {
const fetchDataViews = async () => {
const totalDataViewsList = [];
const dataViewsIds = await data.dataViews.getIds();
for (let i = 0; i < dataViewsIds.length; i++) {
const d = await data.dataViews.get(dataViewsIds[i]);
totalDataViewsList.push(d);
const setCurrentPattern = async () => {
if (activeDatasourceId && datasourceStates[activeDatasourceId].state) {
const dataViewId = datasourceMap[activeDatasourceId].getUsedDataView(
datasourceStates[activeDatasourceId].state
);
const dataView = await data.dataViews.get(dataViewId);
setCurrentIndexPattern(dataView ?? indexPatterns[0]);
}
setDataViewsList(totalDataViewsList);
};
fetchDataViews();
}, [data]);

setCurrentPattern();
}, [activeDatasourceId, datasourceMap, datasourceStates, indexPatterns, data.dataViews]);

useEffect(() => {
if (typeof query === 'object' && query !== null && isOfAggregateQueryType(query)) {
Expand Down Expand Up @@ -847,10 +837,8 @@ export const LensTopNavMenu = ({
onDataViewCreated: createNewDataView,
onCreateDefaultAdHocDataView,
adHocDataViews: indexPatterns.filter((pattern) => !pattern.isPersisted()),
onChangeDataView: (newIndexPatternId: string) => {
const currentDataView = dataViewsList.find(
(indexPattern) => indexPattern.id === newIndexPatternId
);
onChangeDataView: async (newIndexPatternId: string) => {
const currentDataView = await data.dataViews.get(newIndexPatternId);
setCurrentIndexPattern(currentDataView);
dispatchChangeIndexPattern(newIndexPatternId);
if (isOnTextBasedMode) {
Expand All @@ -864,6 +852,39 @@ export const LensTopNavMenu = ({
setIsOnTextBasedMode(false);
}
},
onEditDataView: async (updatedDataViewStub) => {
if (!currentIndexPattern) return;
if (currentIndexPattern.isPersisted()) {
// clear instance cache and fetch again to make sure fields are up to date (in case pattern changed)
dataViewsService.clearInstanceCache(currentIndexPattern.id);
const updatedCurrentIndexPattern = await dataViewsService.get(currentIndexPattern.id!);
// if the data view was persisted, reload it from cache
const updatedCache = {
...dataViews.indexPatterns,
};
delete updatedCache[currentIndexPattern.id!];
const newIndexPatterns = await indexPatternService.ensureIndexPattern({
id: updatedCurrentIndexPattern.id!,
cache: updatedCache,
});
dispatch(
changeIndexPattern({
dataViews: { indexPatterns: newIndexPatterns },
indexPatternId: updatedCurrentIndexPattern.id!,
})
);
// Renew session id to make sure the request is done again
dispatchSetState({
searchSessionId: data.search.session.start(),
resolvedDateRange: getResolvedDateRange(data.query.timefilter.timefilter),
});
// update list of index patterns to pick up mutations in the changed data view
setCurrentIndexPattern(updatedCurrentIndexPattern);
} else {
// if it was an ad-hoc data view, we need to switch to a new data view anyway
indexPatternService.replaceDataViewId(updatedDataViewStub);
}
},
textBasedLanguages: supportedTextBasedLanguages as DataViewPickerProps['textBasedLanguages'],
};

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/mocks/datasource_mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function createMockDatasource(id: string): DatasourceMock {
isTimeBased: jest.fn(),
isValidColumn: jest.fn(),
isEqual: jest.fn(),
getUsedDataView: jest.fn(),
getUsedDataView: jest.fn((state, layer) => 'mockip'),
getUsedDataViews: jest.fn(),
onRefreshIndexPattern: jest.fn(),
};
Expand Down

0 comments on commit 282b7c8

Please sign in to comment.