From b6dbc7712cc5c383bdd2f8392a2423f724068a2a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 21 Jun 2022 05:32:34 -0400 Subject: [PATCH] [Discover] Hide "Add a field", "Edit" and "Create a data view" buttons in viewer mode (#134582) (#134802) * [Discover] Hide "Add a field" button for read only access * [Discover] Hide "Create a data view" button for read only access on desktop * [Discover] Hide "Create a data view" and "Add a field" button for read only access on mobile * [Discover] Make sure that error message is shown when access rights were reduced for a user in meantime * [Discover] Make checks safe * [Discover] Update tests * [Discover] Streamline the logic * [Discover] Update tests * [Discover] Add tests * [Discover] Add tests * [Discover] Update code style Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 7410fbf4d89ad757e2e0a5747a886cc3bb1a896f) Co-authored-by: Julia Rechkunova --- .../components/layout/discover_layout.tsx | 1 - .../components/sidebar/discover_field.tsx | 4 +- .../sidebar/discover_sidebar.test.tsx | 88 ++++++++++++++++++ .../components/sidebar/discover_sidebar.tsx | 58 +++++++----- .../discover_sidebar_responsive.test.tsx | 33 +++++++ .../sidebar/discover_sidebar_responsive.tsx | 91 ++++++++++--------- .../components/top_nav/discover_topnav.tsx | 52 +++++------ .../dataview_picker/change_dataview.test.tsx | 2 +- .../lens/public/app_plugin/app.test.tsx | 4 +- .../lens/public/app_plugin/lens_top_nav.tsx | 75 +++++++++------ 10 files changed, 272 insertions(+), 136 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 6cbc8add99c39..638c3d796a656 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -228,7 +228,6 @@ export function DiscoverLayout({ resetSavedSearch={resetSavedSearch} onChangeIndexPattern={onChangeIndexPattern} onEditRuntimeField={onEditRuntimeField} - useNewFieldsApi={useNewFieldsApi} /> ; /** - * Callback to edit a runtime field from index pattern + * Callback to edit a field from data view * @param fieldName name of the field to edit */ onEditField?: (fieldName: string) => void; /** - * Callback to delete a runtime field from index pattern + * Callback to delete a runtime field from data view * @param fieldName name of the field to delete */ onDeleteField?: (fieldName: string) => void; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx index c26a2c01f256a..6652db105d1de 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.test.tsx @@ -9,6 +9,7 @@ import { cloneDeep, each } from 'lodash'; import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; +import { Action } from '@kbn/ui-actions-plugin/public'; // @ts-expect-error import realHits from '../../../../__fixtures__/real_hits'; @@ -29,6 +30,16 @@ import { BehaviorSubject } from 'rxjs'; import { FetchStatus } from '../../../types'; import { AvailableFields$ } from '../../utils/use_saved_search'; +const mockGetActions = jest.fn>>, [string, { fieldName: string }]>( + () => Promise.resolve([]) +); + +jest.mock('../../../../kibana_services', () => ({ + getUiActions: () => ({ + getTriggerCompatibleActions: mockGetActions, + }), +})); + function getCompProps(): DiscoverSidebarProps { const indexPattern = stubLogstashIndexPattern; const hits = each(cloneDeep(realHits), (hit) => @@ -73,6 +84,7 @@ function getCompProps(): DiscoverSidebarProps { createNewDataView: jest.fn(), onDataViewCreated: jest.fn(), availableFields$, + useNewFieldsApi: true, }; } @@ -105,4 +117,80 @@ describe('discover sidebar', function () { findTestSubject(comp, 'fieldToggle-extension').simulate('click'); expect(props.onRemoveField).toHaveBeenCalledWith('extension'); }); + + it('should render "Add a field" button', () => { + const addFieldButton = findTestSubject(comp, 'indexPattern-add-field_btn'); + expect(addFieldButton.length).toBe(1); + addFieldButton.simulate('click'); + expect(props.editField).toHaveBeenCalledWith(); + }); + + it('should render "Edit field" button', () => { + findTestSubject(comp, 'field-bytes').simulate('click'); + const editFieldButton = findTestSubject(comp, 'discoverFieldListPanelEdit-bytes'); + expect(editFieldButton.length).toBe(1); + editFieldButton.simulate('click'); + expect(props.editField).toHaveBeenCalledWith('bytes'); + }); + + it('should not render Add/Edit field buttons in viewer mode', () => { + const compInViewerMode = mountWithIntl( + + + + ); + const addFieldButton = findTestSubject(compInViewerMode, 'indexPattern-add-field_btn'); + expect(addFieldButton.length).toBe(0); + findTestSubject(comp, 'field-bytes').simulate('click'); + const editFieldButton = findTestSubject(compInViewerMode, 'discoverFieldListPanelEdit-bytes'); + expect(editFieldButton.length).toBe(0); + }); + + it('should render buttons in data view picker correctly', async () => { + const compWithPicker = mountWithIntl( + + + + ); + // open data view picker + findTestSubject(compWithPicker, 'indexPattern-switch-link').simulate('click'); + expect(findTestSubject(compWithPicker, 'changeDataViewPopover').length).toBe(1); + // click "Add a field" + const addFieldButtonInDataViewPicker = findTestSubject( + compWithPicker, + 'indexPattern-add-field' + ); + expect(addFieldButtonInDataViewPicker.length).toBe(1); + addFieldButtonInDataViewPicker.simulate('click'); + expect(props.editField).toHaveBeenCalledWith(); + // click "Create a data view" + const createDataViewButton = findTestSubject(compWithPicker, 'dataview-create-new'); + expect(createDataViewButton.length).toBe(1); + createDataViewButton.simulate('click'); + expect(props.createNewDataView).toHaveBeenCalled(); + }); + + it('should not render buttons in data view picker when in viewer mode', async () => { + const compWithPickerInViewerMode = mountWithIntl( + + + + ); + // open data view picker + findTestSubject(compWithPickerInViewerMode, 'indexPattern-switch-link').simulate('click'); + expect(findTestSubject(compWithPickerInViewerMode, 'changeDataViewPopover').length).toBe(1); + // check that buttons are not present + const addFieldButtonInDataViewPicker = findTestSubject( + compWithPickerInViewerMode, + 'indexPattern-add-field' + ); + expect(addFieldButtonInDataViewPicker.length).toBe(0); + const createDataViewButton = findTestSubject(compWithPickerInViewerMode, 'dataview-create-new'); + expect(createDataViewButton.length).toBe(0); + }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index 585fa79783ace..7bda0e534cf2b 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -68,9 +68,18 @@ export interface DiscoverSidebarProps extends Omit void | undefined) => void; - editField: (fieldName?: string) => void; + /** + * Handles "Edit field" action + * Buttons will be hidden if not provided + * @param fieldName + */ + editField?: (fieldName?: string) => void; - createNewDataView: () => void; + /** + * Handles "Create a data view action" action + * Buttons will be hidden if not provided + */ + createNewDataView?: () => void; /** * a statistics of the distribution of fields in the given hits @@ -113,9 +122,6 @@ export function DiscoverSidebarComponent({ }: DiscoverSidebarProps) { const { uiSettings, dataViewFieldEditor } = useDiscoverServices(); const [fields, setFields] = useState(null); - - const dataViewFieldEditPermission = dataViewFieldEditor?.userPermissions.editIndexPattern(); - const canEditDataViewField = !!dataViewFieldEditPermission && useNewFieldsApi; const [scrollContainer, setScrollContainer] = useState(null); const [fieldsToRender, setFieldsToRender] = useState(FIELDS_PER_PAGE); const [fieldsPerPage, setFieldsPerPage] = useState(FIELDS_PER_PAGE); @@ -258,7 +264,7 @@ export function DiscoverSidebarComponent({ const deleteField = useMemo( () => - canEditDataViewField && selectedIndexPattern + editField && selectedIndexPattern ? async (fieldName: string) => { const ref = dataViewFieldEditor.openDeleteModal({ ctx: { @@ -279,7 +285,7 @@ export function DiscoverSidebarComponent({ : undefined, [ selectedIndexPattern, - canEditDataViewField, + editField, setFieldEditorRef, closeFlyout, onEditRuntimeField, @@ -396,8 +402,8 @@ export function DiscoverSidebarComponent({ selected={true} trackUiMetric={trackUiMetric} multiFields={multiFields?.get(field.name)} - onEditField={canEditDataViewField ? editField : undefined} - onDeleteField={canEditDataViewField ? deleteField : undefined} + onEditField={editField} + onDeleteField={deleteField} showFieldStats={showFieldStats} /> @@ -456,8 +462,8 @@ export function DiscoverSidebarComponent({ getDetails={getDetailsByField} trackUiMetric={trackUiMetric} multiFields={multiFields?.get(field.name)} - onEditField={canEditDataViewField ? editField : undefined} - onDeleteField={canEditDataViewField ? deleteField : undefined} + onEditField={editField} + onDeleteField={deleteField} showFieldStats={showFieldStats} /> @@ -485,8 +491,8 @@ export function DiscoverSidebarComponent({ getDetails={getDetailsByField} trackUiMetric={trackUiMetric} multiFields={multiFields?.get(field.name)} - onEditField={canEditDataViewField ? editField : undefined} - onDeleteField={canEditDataViewField ? deleteField : undefined} + onEditField={editField} + onDeleteField={deleteField} showFieldStats={showFieldStats} /> @@ -498,18 +504,20 @@ export function DiscoverSidebarComponent({ )} - - editField()} - size="s" - > - {i18n.translate('discover.fieldChooser.addField.label', { - defaultMessage: 'Add a field', - })} - - + {!!editField && ( + + editField()} + size="s" + > + {i18n.translate('discover.fieldChooser.addField.label', { + defaultMessage: 'Add a field', + })} + + + )} ); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index d3af81c06c129..44cd82fad699d 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -52,6 +52,11 @@ const mockServices = { }, }, docLinks: { links: { discover: { fieldTypeHelp: '' } } }, + dataViewEditor: { + userPermissions: { + editDataView: jest.fn(() => true), + }, + }, } as unknown as DiscoverServices; const mockfieldCounts: Record = {}; @@ -111,6 +116,7 @@ function getCompProps(): DiscoverSidebarResponsiveProps { onEditRuntimeField: jest.fn(), viewMode: VIEW_MODE.DOCUMENT_LEVEL, onDataViewCreated: jest.fn(), + useNewFieldsApi: true, }; } @@ -160,4 +166,31 @@ describe('discover responsive sidebar', function () { expect(findTestSubject(comp, 'fieldList-unpopular').children().length).toBe(4); expect(mockCalcFieldCounts.mock.calls.length).toBe(1); }); + + it('should show "Add a field" button to create a runtime field', () => { + expect(mockServices.dataViewEditor.userPermissions.editDataView).toHaveBeenCalled(); + expect(findTestSubject(comp, 'indexPattern-add-field_btn').length).toBe(1); + }); + + it('should not show "Add a field" button in viewer mode', () => { + const mockedServicesInViewerMode = { + ...mockServices, + dataViewEditor: { + ...mockServices.dataViewEditor, + userPermissions: { + ...mockServices.dataViewEditor.userPermissions, + editDataView: jest.fn(() => false), + }, + }, + }; + const compInViewerMode = mountWithIntl( + + + + ); + expect( + mockedServicesInViewerMode.dataViewEditor.userPermissions.editDataView + ).toHaveBeenCalled(); + expect(findTestSubject(compInViewerMode, 'indexPattern-add-field_btn').length).toBe(0); + }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 785834785572b..5c02efe16ecc9 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useEffect, useRef, useState, useCallback } from 'react'; +import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { UiCounterMetricType } from '@kbn/analytics'; @@ -89,7 +89,7 @@ export interface DiscoverSidebarResponsiveProps { /** * Read from the Fields API */ - useNewFieldsApi?: boolean; + useNewFieldsApi: boolean; /** * callback to execute on edit runtime field */ @@ -115,7 +115,7 @@ export interface DiscoverSidebarResponsiveProps { */ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) { const services = useDiscoverServices(); - const { selectedIndexPattern, onEditRuntimeField, useNewFieldsApi, onDataViewCreated } = props; + const { selectedIndexPattern, onEditRuntimeField, onDataViewCreated } = props; const [fieldFilter, setFieldFilter] = useState(getDefaultFieldFilter()); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); /** @@ -178,6 +178,8 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) const { dataViewFieldEditor, dataViewEditor } = services; const { availableFields$ } = props; + const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); + useEffect( () => { // For an external embeddable like the Field stats @@ -203,57 +205,56 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) ] ); - const editField = useCallback( - (fieldName?: string) => { - const indexPatternFieldEditPermission = - dataViewFieldEditor?.userPermissions.editIndexPattern(); - const canEditIndexPatternField = !!indexPatternFieldEditPermission && useNewFieldsApi; - if (!canEditIndexPatternField || !selectedIndexPattern) { - return; - } - const ref = dataViewFieldEditor.openEditor({ - ctx: { - dataView: selectedIndexPattern, - }, - fieldName, - onSave: async () => { - onEditRuntimeField(); - }, - }); - if (setFieldEditorRef) { - setFieldEditorRef(ref); - } - if (closeFlyout) { - closeFlyout(); - } - }, + const editField = useMemo( + () => + canEditDataView && selectedIndexPattern + ? (fieldName?: string) => { + const ref = dataViewFieldEditor.openEditor({ + ctx: { + dataView: selectedIndexPattern, + }, + fieldName, + onSave: async () => { + onEditRuntimeField(); + }, + }); + if (setFieldEditorRef) { + setFieldEditorRef(ref); + } + if (closeFlyout) { + closeFlyout(); + } + } + : undefined, [ + canEditDataView, closeFlyout, dataViewFieldEditor, selectedIndexPattern, setFieldEditorRef, onEditRuntimeField, - useNewFieldsApi, ] ); - const createNewDataView = useCallback(() => { - const indexPatternFieldEditPermission = dataViewEditor.userPermissions.editDataView; - if (!indexPatternFieldEditPermission) { - return; - } - const ref = dataViewEditor.openEditor({ - onSave: async (dataView) => { - onDataViewCreated(dataView); - }, - }); - if (setDataViewEditorRef) { - setDataViewEditorRef(ref); - } - if (closeFlyout) { - closeFlyout(); - } - }, [dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated]); + const createNewDataView = useMemo( + () => + canEditDataView + ? () => { + const ref = dataViewEditor.openEditor({ + onSave: async (dataView) => { + onDataViewCreated(dataView); + }, + }); + if (setDataViewEditorRef) { + setDataViewEditorRef(ref); + } + if (closeFlyout) { + closeFlyout(); + } + } + : undefined, + [canEditDataView, dataViewEditor, setDataViewEditorRef, closeFlyout, onDataViewCreated] + ); if (!selectedIndexPattern) { return null; diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 87d2f04bd604b..443bf92e609a4 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -27,7 +27,6 @@ export type DiscoverTopNavProps = Pick< resetSavedSearch: () => void; onChangeIndexPattern: (indexPattern: string) => void; onEditRuntimeField: () => void; - useNewFieldsApi?: boolean; }; export const DiscoverTopNav = ({ @@ -43,7 +42,6 @@ export const DiscoverTopNav = ({ resetSavedSearch, onChangeIndexPattern, onEditRuntimeField, - useNewFieldsApi = false, }: DiscoverTopNavProps) => { const history = useHistory(); const showDatePicker = useMemo( @@ -52,11 +50,9 @@ export const DiscoverTopNav = ({ ); const services = useDiscoverServices(); const { dataViewEditor, navigation, dataViewFieldEditor, data } = services; - const editPermission = useMemo( - () => dataViewFieldEditor.userPermissions.editIndexPattern(), - [dataViewFieldEditor] - ); - const canEditDataViewField = !!editPermission && useNewFieldsApi; + + const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); + const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -87,7 +83,7 @@ export const DiscoverTopNav = ({ const editField = useMemo( () => - canEditDataViewField + canEditDataView ? async (fieldName?: string, uiAction: 'edit' | 'add' = 'edit') => { if (indexPattern?.id) { const indexPatternInstance = await data.dataViews.get(indexPattern.id); @@ -103,33 +99,29 @@ export const DiscoverTopNav = ({ } } : undefined, - [ - canEditDataViewField, - indexPattern?.id, - data.dataViews, - dataViewFieldEditor, - onEditRuntimeField, - ] + [canEditDataView, indexPattern?.id, data.dataViews, dataViewFieldEditor, onEditRuntimeField] ); const addField = useMemo( - () => (canEditDataViewField && editField ? () => editField(undefined, 'add') : undefined), - [editField, canEditDataViewField] + () => (canEditDataView && editField ? () => editField(undefined, 'add') : undefined), + [editField, canEditDataView] ); - const createNewDataView = useCallback(() => { - const indexPatternFieldEditPermission = dataViewEditor.userPermissions.editDataView; - if (!indexPatternFieldEditPermission) { - return; - } - closeDataViewEditor.current = dataViewEditor.openEditor({ - onSave: async (dataView) => { - if (dataView.id) { - onChangeIndexPattern(dataView.id); - } - }, - }); - }, [dataViewEditor, onChangeIndexPattern]); + const createNewDataView = useMemo( + () => + canEditDataView + ? () => { + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataView) => { + if (dataView.id) { + onChangeIndexPattern(dataView.id); + } + }, + }); + } + : undefined, + [canEditDataView, dataViewEditor, onChangeIndexPattern] + ); const topNavMenu = useMemo( () => diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx index d3081561a0c4e..f567f3b6ac47b 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx @@ -116,7 +116,7 @@ describe('DataView component', () => { await act(async () => { const component = mount(wrapDataViewComponentInContext(props, true)); findTestSubject(component, 'dataview-trigger').simulate('click'); - expect(component.find('[data-test-subj="idataview-create-new"]').length).toBe(0); + expect(component.find('[data-test-subj="dataview-create-new"]').length).toBe(0); }); }); diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index fed226af7dde5..a693aa4ada745 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -409,7 +409,7 @@ describe('Lens App', () => { expect.objectContaining({ currentDataViewId: 'mockip', onChangeDataView: expect.any(Function), - onDataViewCreated: expect.any(Function), + onDataViewCreated: undefined, onAddField: undefined, }) ); @@ -417,7 +417,7 @@ describe('Lens App', () => { it('calls the nav component with the correct dataview picker props if permissions are given', async () => { const { instance, lensStore, services } = await mountWith({ preloadedState: {} }); - services.dataViewFieldEditor.userPermissions.editIndexPattern = () => true; + services.dataViewEditor.userPermissions.editDataView = () => true; const document = { savedObjectId: defaultSavedObjectId, state: { diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index c17417e5106a1..3ac59c03df4c1 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -244,7 +244,7 @@ export const LensTopNavMenu = ({ const [indexPatterns, setIndexPatterns] = useState([]); const [currentIndexPattern, setCurrentIndexPattern] = useState(); const [rejectedIndexPatterns, setRejectedIndexPatterns] = useState([]); - const editPermission = dataViewFieldEditor.userPermissions.editIndexPattern(); + const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -644,7 +644,7 @@ export const LensTopNavMenu = ({ const editField = useMemo( () => - editPermission + canEditDataView ? async (fieldName?: string, uiAction: 'edit' | 'add' = 'edit') => { if (currentIndexPattern?.id) { const indexPatternInstance = await data.dataViews.get(currentIndexPattern?.id); @@ -660,39 +660,54 @@ export const LensTopNavMenu = ({ } } : undefined, - [editPermission, currentIndexPattern?.id, data.dataViews, dataViewFieldEditor, refreshFieldList] + [ + canEditDataView, + currentIndexPattern?.id, + data.dataViews, + dataViewFieldEditor, + refreshFieldList, + ] ); const addField = useMemo( - () => (editPermission && editField ? () => editField(undefined, 'add') : undefined), - [editField, editPermission] + () => (canEditDataView && editField ? () => editField(undefined, 'add') : undefined), + [editField, canEditDataView] ); - const createNewDataView = useCallback(() => { - const dataViewEditPermission = dataViewEditor.userPermissions.editDataView; - if (!dataViewEditPermission) { - return; - } - closeDataViewEditor.current = dataViewEditor.openEditor({ - onSave: async (dataView) => { - if (dataView.id) { - handleIndexPatternChange({ - activeDatasources: Object.keys(datasourceStates).reduce( - (acc, datasourceId) => ({ - ...acc, - [datasourceId]: datasourceMap[datasourceId], - }), - {} - ), - datasourceStates, - indexPatternId: dataView.id, - setDatasourceState, - }); - refreshFieldList(); - } - }, - }); - }, [dataViewEditor, datasourceMap, datasourceStates, refreshFieldList, setDatasourceState]); + const createNewDataView = useMemo( + () => + canEditDataView + ? () => { + closeDataViewEditor.current = dataViewEditor.openEditor({ + onSave: async (dataView) => { + if (dataView.id) { + handleIndexPatternChange({ + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, datasourceId) => ({ + ...acc, + [datasourceId]: datasourceMap[datasourceId], + }), + {} + ), + datasourceStates, + indexPatternId: dataView.id, + setDatasourceState, + }); + refreshFieldList(); + } + }, + }); + } + : undefined, + [ + dataViewEditor, + canEditDataView, + datasourceMap, + datasourceStates, + refreshFieldList, + setDatasourceState, + ] + ); const dataViewPickerProps = { trigger: {