From 85721fbb2f7d078790dda0295173ad5b86e4ff62 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Wed, 20 Dec 2023 11:01:19 -0700 Subject: [PATCH 1/7] Focus on title input in panel settings flyout when title clicked --- .../customize_panel_action.tsx | 20 ++++---- .../customize_panel_editor.tsx | 50 +++++++++++-------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx index 63fc1902102b6..8ae7e5c637aa1 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx @@ -6,23 +6,23 @@ * Side Public License, v 1. */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; +import { OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { TimeRange } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; -import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { Action, IncompatibleActionError, Trigger } from '@kbn/ui-actions-plugin/public'; +import React from 'react'; -import { core } from '../../../kibana_services'; import { - IEmbeddable, + EditPanelAction, Embeddable, EmbeddableInput, EmbeddableOutput, - EditPanelAction, + IEmbeddable, } from '../../..'; -import { ViewMode, CommonlyUsedRange } from '../../../lib/types'; +import { core } from '../../../kibana_services'; +import { CommonlyUsedRange, ViewMode } from '../../../lib/types'; import { tracksOverlays } from '../track_overlays'; import { CustomizePanelEditor } from './customize_panel_editor'; @@ -50,6 +50,7 @@ export function hasTimeRange( export interface CustomizePanelActionContext { embeddable: IEmbeddable | Embeddable; + trigger?: Trigger; } export class CustomizePanelAction implements Action { @@ -104,7 +105,7 @@ export class CustomizePanelAction implements Action ); } - public async execute({ embeddable }: CustomizePanelActionContext) { + public async execute({ embeddable, trigger }: CustomizePanelActionContext) { const isCompatible = await this.isCompatible({ embeddable }); if (!isCompatible) { throw new IncompatibleActionError(); @@ -126,6 +127,7 @@ export class CustomizePanelAction implements Action toMountPoint( void; onEdit: () => void; + titleFocus?: boolean; } export const CustomizePanelEditor = (props: CustomizePanelProps) => { - const { onClose, embeddable, dateFormat, timeRangeCompatible, onEdit } = props; + const { onClose, embeddable, dateFormat, timeRangeCompatible, onEdit, titleFocus } = props; const editMode = embeddable.getInput().viewMode === ViewMode.EDIT; const [hideTitle, setHideTitle] = useState(embeddable.getInput().hidePanelTitles); const [panelDescription, setPanelDescription] = useState( @@ -75,6 +77,13 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { ? (embeddable as Embeddable).getInput().timeRange : undefined ); + const inputRef = useRef(null); + + useEffect(() => { + if (titleFocus && inputRef.current) { + inputRef.current.focus(); + } + }, [inputRef, titleFocus]); const commonlyUsedRangesForDatePicker = props.commonlyUsedRanges ? props.commonlyUsedRanges.map( @@ -154,6 +163,7 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { } > Date: Wed, 20 Dec 2023 15:51:17 -0700 Subject: [PATCH 2/7] Fix import --- .../customize_panel_action/customize_panel_editor.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx index b7800ab2326d5..419423660d3c0 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx @@ -7,7 +7,6 @@ */ import React, { useEffect, useRef, useState } from 'react'; -import useMount from 'react-use/lib/useMount'; import { EuiButton, From 95f56ed12528391eb2b789b86c2f778502f5f143 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Tue, 2 Jan 2024 14:11:16 -0700 Subject: [PATCH 3/7] Add tests --- .../customize_panel_action.tsx | 2 +- .../customize_panel_editor.test.tsx | 204 ++++++++++++++++++ .../customize_panel_editor.tsx | 6 +- .../filters_details.tsx | 3 + .../embeddable_panel_header.test.tsx | 174 +++++++++++++++ .../panel_header/embeddable_panel_header.tsx | 2 +- .../panel_header/embeddable_panel_title.tsx | 10 +- .../apps/dashboard/group2/panel_time_range.ts | 14 -- .../apps/dashboard/group2/panel_titles.ts | 38 ---- 9 files changed, 394 insertions(+), 59 deletions(-) create mode 100644 src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx create mode 100644 src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.test.tsx diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx index 8ae7e5c637aa1..a7fa84aa54188 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx @@ -127,7 +127,7 @@ export class CustomizePanelAction implements Action toMountPoint( null) as any, {} as any); +const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock); +customizePanelAction.execute = jest.fn(); + +const DEFAULT_PANEL_TITLE = 'Panel title'; + +const createEmbeddable = async ( + initialInput?: Partial +): Promise => { + return await mockEmbeddableFactory.create({ + id: '20', + firstName: 'Bilbo', + lastName: 'Baggins', + title: DEFAULT_PANEL_TITLE, + ...initialInput, + }); +}; + +const DEFAULT_PROPS = { + timeRangeCompatible: true, + onClose: jest.fn(), + onEdit: jest.fn(), +}; + +describe('panel title / description', () => { + test('does not render if in view mode', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.VIEW }); + render( + + + + ); + + const customizePanelForm = await screen.findByTestId('customizePanelForm'); + const titleDescriptionComponent = screen.queryByTestId('customEmbeddableTitleComponent'); + expect(customizePanelForm).not.toContainElement(titleDescriptionComponent); + }); + + test('title input receives focus when `titleFocus` is `true`', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + + + ); + + const customTitleComponent = await screen.findByTestId('customEmbeddablePanelTitleInput'); + expect(customTitleComponent).toHaveFocus(); + }); + + test('title input does not receive focus when `titleFocus` is `false`', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + + + ); + + const customTitleComponent = await screen.findByTestId('customEmbeddablePanelTitleInput'); + expect(customTitleComponent).not.toHaveFocus(); + }); +}); + +describe('custom time picker', () => { + test('renders custom time picker if embeddable supports it', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + + + ); + + const customTimeRangeComponent = await screen.findByTestId('customizePanelTimeRangeDatePicker'); + expect(customTimeRangeComponent).toBeDefined(); + }); + + test('does not render custom time picker if embeddable does not support it', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + + + ); + + const customizePanelForm = await screen.findByTestId('customizePanelForm'); + const customTimeRangeComponent = screen.queryByTestId('customizePanelTimeRangeDatePicker'); + expect(customizePanelForm).not.toContainElement(customTimeRangeComponent); + }); + + test('does not render filters and/or query info if embeddable does not support it', async () => { + const mockEmbeddable = await createEmbeddable({ + viewMode: ViewMode.EDIT, + }); + + render( + + + + ); + + const customizePanelForm = await screen.findByTestId('customizePanelForm'); + const customPanelQuery = screen.queryByTestId('panelCustomQueryRow'); + expect(customizePanelForm).not.toContainElement(customPanelQuery); + const customPanelFilters = screen.queryByTestId('panelCustomFiltersRow'); + expect(customizePanelForm).not.toContainElement(customPanelFilters); + }); + + describe('filterable embeddable', () => { + test('renders custom filters, if provided', async () => { + const mockEmbeddable: FilterableEmbeddable = (await createEmbeddable({ + viewMode: ViewMode.EDIT, + })) as unknown as FilterableEmbeddable; + + mockEmbeddable.getFilters = jest.fn().mockResolvedValue([ + { + meta: {}, + query: {}, + $state: {}, + }, + ] as Filter[]); + mockEmbeddable.getQuery = jest.fn().mockResolvedValue({}); + render( + + + + ); + await waitFor(() => { + expect(screen.getByTestId('euiSkeletonLoadingAriaWrapper')).toBeInTheDocument(); + }); + const customPanelQuery = await screen.findByTestId('panelCustomFiltersRow'); + expect(customPanelQuery).toBeInTheDocument(); + }); + + test('renders a custom query, if provided', async () => { + const mockEmbeddable: FilterableEmbeddable = (await createEmbeddable({ + viewMode: ViewMode.EDIT, + })) as unknown as FilterableEmbeddable; + mockEmbeddable.getFilters = jest.fn().mockResolvedValue([]); + mockEmbeddable.getQuery = jest.fn().mockResolvedValue({ query: 'field : value' }); + render( + + + + ); + await waitFor(() => { + expect(screen.getByTestId('euiSkeletonLoadingAriaWrapper')).toBeInTheDocument(); + }); + const customPanelQuery = await screen.findByTestId('customPanelQuery'); + expect(customPanelQuery).toHaveTextContent('field : value'); + }); + }); +}); diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx index 419423660d3c0..6bd6350ef08d7 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx @@ -116,7 +116,7 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { if (!editMode) return null; return ( - <> +
{ )} /> - +
); }; @@ -301,7 +301,7 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { - + {renderCustomTitleComponent()} {renderCustomTimeRangeComponent()} {renderFilterDetails()} diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/filters_details.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/filters_details.tsx index 2f151285fe488..300ed9253f0b9 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/filters_details.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/filters_details.tsx @@ -89,6 +89,7 @@ export function FiltersDetails({ embeddable, editMode, onEdit }: FiltersDetailsP {queryString !== '' && ( 0 && ( null) as any, {} as any); +const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock); +customizePanelAction.execute = jest.fn(); + +const DEFAULT_PANEL_TITLE = 'Panel title'; + +const createEmbeddable = async ( + initialInput?: Partial +): Promise => { + return await mockEmbeddableFactory.create({ + id: '20', + firstName: 'Bilbo', + lastName: 'Baggins', + title: DEFAULT_PANEL_TITLE, + ...initialInput, + }); +}; + +describe('view mode', () => { + test('renders as expected', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.VIEW }); + render( + + ); + const titleComponent = await screen.findByTestId('dashboardPanelTitle'); + expect(titleComponent).toHaveTextContent(DEFAULT_PANEL_TITLE); + }); + + test('renders tooltip + icon when description provided', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.VIEW }); + mockEmbeddable.updateOutput({ description: 'This is a description ' }); + render( + + ); + + expect(await screen.findByTestId('embeddablePanelTooltipAnchor')).toBeInTheDocument(); + expect(await screen.findByTestId('embeddablePanelTitleDescriptionIcon')).toBeInTheDocument(); + }); + + test('blank titles are hidden in view mode', async () => { + const mockEmbeddable = await createEmbeddable({ title: '', viewMode: ViewMode.VIEW }); + render( + + ); + + const header = await screen.findByTestId('embeddablePanelHeading'); + const titleComponent = screen.queryByTestId('dashboardPanelTitle'); + expect(header).not.toContainElement(titleComponent); + }); + + test('hiding an individual panel title hides it in view mode', async () => { + const mockEmbeddable = await createEmbeddable({ + viewMode: ViewMode.VIEW, + hidePanelTitles: true, + }); + render( + + ); + + const header = await screen.findByTestId('embeddablePanelHeading'); + const titleComponent = screen.queryByTestId('dashboardPanelTitle'); + expect(header).not.toContainElement(titleComponent); + }); +}); + +describe('edit mode', () => { + test('renders as expected', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + ); + const titleComponent = await screen.findByTestId('dashboardPanelTitle'); + expect(titleComponent).toHaveTextContent(DEFAULT_PANEL_TITLE); + }); + + test('blank titles render [No title] in edit mode', async () => { + const mockEmbeddable = await createEmbeddable({ title: '', viewMode: ViewMode.EDIT }); + render( + + ); + const titleComponent = await screen.findByTestId('dashboardPanelTitle'); + expect(titleComponent).toHaveTextContent('[No Title]'); + }); + + test('hiding an individual panel title renders **only** the context menu button in edit mode', async () => { + const mockEmbeddable = await createEmbeddable({ + viewMode: ViewMode.EDIT, + hidePanelTitles: true, + }); + render( + + ); + screen.debug(); + const titleComponent = await screen.findByTestId('embeddablePanelHeading-'); + const innerTitleComponent = await screen.findByTestId('embeddablePanelTitleInner'); + expect(innerTitleComponent).toBeEmptyDOMElement(); + const menuComponent = await screen.findByTestId('embeddablePanelToggleMenuIcon'); + expect(titleComponent).toContainElement(menuComponent); + }); + + test('clicking title calls customize panel action', async () => { + const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); + render( + + ); + screen.debug(); + const titleComponent = await screen.findByTestId('embeddablePanelTitleLink'); + userEvent.click(titleComponent); + expect(customizePanelAction.execute).toBeCalled(); + }); +}); diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx index ee27bbca0605a..759e8d35a0726 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx @@ -85,7 +85,7 @@ export const EmbeddablePanelHeader = ({ if (!showPanelBar) { return ( -
+
{embeddablePanelContextMenu} {ariaLabelElement}
diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx index 734b420d04052..4341f5aa4a983 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx @@ -56,15 +56,21 @@ export const EmbeddablePanelTitle = ({ }, [customizePanelAction, embeddable, title, viewMode, hideTitle]); const titleComponentWithDescription = useMemo(() => { - if (!description) return {titleComponent}; + if (!description) + return ( + + {titleComponent} + + ); return ( - + {titleComponent}{' '} { - it('should not show custom time picker in flyout', async () => { - await dashboardPanelActions.removePanel(); - await PageObjects.dashboard.waitForRenderComplete(); - await dashboardAddPanel.clickMarkdownQuickButton(); - await PageObjects.visEditor.setMarkdownTxt('I am timeless!'); - await PageObjects.visEditor.clickGo(); - await PageObjects.visualize.saveVisualizationAndReturn(); - await PageObjects.dashboard.clickQuickSave(); - await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.expectMissingCustomTimeRange(); - }); - }); }); } diff --git a/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts b/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts index 2c0ac33107fea..4d7950042783a 100644 --- a/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts +++ b/x-pack/test/functional/apps/dashboard/group2/panel_titles.ts @@ -78,44 +78,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(panelTitle).to.equal(EMPTY_TITLE); await PageObjects.dashboard.clearUnsavedChanges(); }); - - it('blank titles are hidden in view mode', async () => { - await PageObjects.dashboard.clickCancelOutOfEditMode(); - - const titleVisibility = (await PageObjects.dashboard.getVisibilityOfPanelTitles())[0]; - expect(titleVisibility).to.be(false); - }); - - it('custom titles are visible in view mode', async () => { - await PageObjects.dashboard.switchToEditMode(); - await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.setCustomPanelTitle(CUSTOM_TITLE); - await dashboardCustomizePanel.clickSaveButton(); - await PageObjects.dashboard.clickQuickSave(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); - - const titleVisibility = (await PageObjects.dashboard.getVisibilityOfPanelTitles())[0]; - expect(titleVisibility).to.be(true); - }); - - it('hiding an individual panel title hides it in view mode', async () => { - await PageObjects.dashboard.switchToEditMode(); - await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleHidePanelTitle(); - await dashboardCustomizePanel.clickSaveButton(); - await PageObjects.dashboard.clickQuickSave(); - await PageObjects.dashboard.clickCancelOutOfEditMode(); - - const titleVisibility = (await PageObjects.dashboard.getVisibilityOfPanelTitles())[0]; - expect(titleVisibility).to.be(false); - - // undo the previous hide panel toggle (i.e. make the panel visible) to keep state consistent - await PageObjects.dashboard.switchToEditMode(); - await dashboardPanelActions.customizePanel(); - await dashboardCustomizePanel.clickToggleHidePanelTitle(); - await dashboardCustomizePanel.clickSaveButton(); - await PageObjects.dashboard.clickQuickSave(); - }); }); describe('by reference', () => { From 136529491afbde4ec93e470b556b561a8ceeee58 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Tue, 2 Jan 2024 15:15:21 -0700 Subject: [PATCH 4/7] Clean up --- x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts b/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts index 7181dafc483e1..b0dbc3645ee8f 100644 --- a/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts +++ b/x-pack/test/functional/apps/dashboard/group2/panel_time_range.ts @@ -15,7 +15,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardPanelActions = getService('dashboardPanelActions'); const dashboardBadgeActions = getService('dashboardBadgeActions'); const dashboardCustomizePanel = getService('dashboardCustomizePanel'); - const dashboardAddPanel = getService('dashboardAddPanel'); const PageObjects = getPageObjects([ 'common', 'dashboard', From e9198dcbe833f3a518ef58c89f422f44e7197025 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Thu, 4 Jan 2024 10:35:45 -0700 Subject: [PATCH 5/7] Implement feedback --- .../embeddable_panel/embeddable_panel.tsx | 29 ++--- .../custom_time_range_badge.test.ts | 31 +---- .../custom_time_range_badge.tsx | 12 +- .../customize_panel_action.test.ts | 15 +-- .../customize_panel_action.tsx | 120 ++---------------- .../customize_panel_editor.test.tsx | 6 +- .../customize_panel_editor.tsx | 16 +-- .../open_customize_panel.tsx | 75 +++++++++++ .../time_range_helpers.ts | 53 ++++++++ .../panel_header/embeddable_panel_header.tsx | 2 +- .../panel_header/embeddable_panel_title.tsx | 19 ++- src/plugins/embeddable/public/plugin.tsx | 11 +- 12 files changed, 193 insertions(+), 196 deletions(-) create mode 100644 src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/open_customize_panel.tsx create mode 100644 src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/time_range_helpers.ts diff --git a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx index c24383ff9fb18..134b65efc9d86 100644 --- a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.tsx @@ -6,20 +6,23 @@ * Side Public License, v 1. */ -import { isNil } from 'lodash'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, htmlIdGenerator } from '@elastic/eui'; import classNames from 'classnames'; -import { distinct, map } from 'rxjs'; +import { isNil } from 'lodash'; import React, { ReactNode, useEffect, useMemo, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, htmlIdGenerator } from '@elastic/eui'; +import { distinct, map } from 'rxjs'; -import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { PanelLoader } from '@kbn/panel-loader'; +import { core, embeddableStart, inspector } from '../kibana_services'; +import { EmbeddableErrorHandler, EmbeddableOutput, ViewMode } from '../lib'; +import { EmbeddablePanelError } from './embeddable_panel_error'; import { + CustomizePanelAction, EditPanelAction, - RemovePanelAction, InspectPanelAction, - CustomizePanelAction, + RemovePanelAction, } from './panel_actions'; +import { EmbeddablePanelHeader } from './panel_header/embeddable_panel_header'; import { EmbeddablePhase, EmbeddablePhaseEvent, @@ -30,10 +33,6 @@ import { useSelectFromEmbeddableInput, useSelectFromEmbeddableOutput, } from './use_select_from_embeddable'; -import { EmbeddablePanelError } from './embeddable_panel_error'; -import { core, embeddableStart, inspector } from '../kibana_services'; -import { ViewMode, EmbeddableErrorHandler, EmbeddableOutput } from '../lib'; -import { EmbeddablePanelHeader } from './panel_header/embeddable_panel_header'; const getEventStatus = (output: EmbeddableOutput): EmbeddablePhase => { if (!isNil(output.error)) { @@ -61,8 +60,6 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { * bypass the trigger registry. */ const universalActions = useMemo(() => { - const commonlyUsedRanges = core.uiSettings.get(UI_SETTINGS.TIMEPICKER_QUICK_RANGES); - const dateFormat = core.uiSettings.get(UI_SETTINGS.DATE_FORMAT); const stateTransfer = embeddableStart.getStateTransfer(); const editPanel = new EditPanelAction( embeddableStart.getEmbeddableFactory, @@ -71,13 +68,7 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => { ); const actions: PanelUniversalActions = { - customizePanel: new CustomizePanelAction( - core.overlays, - core.theme, - editPanel, - commonlyUsedRanges, - dateFormat - ), + customizePanel: new CustomizePanelAction(editPanel), removePanel: new RemovePanelAction(), editPanel, }; diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.test.ts b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.test.ts index 454c92a602691..1c1b7226b7f30 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.test.ts +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.test.ts @@ -6,16 +6,13 @@ * Side Public License, v 1. */ -import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; -import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; - import { - TimeRangeEmbeddable, TimeRangeContainer, + TimeRangeEmbeddable, TIME_RANGE_EMBEDDABLE, } from '../../../lib/test_samples/embeddables'; -import { CustomTimeRangeBadge } from './custom_time_range_badge'; import { EditPanelAction } from '../edit_panel_action/edit_panel_action'; +import { CustomTimeRangeBadge } from './custom_time_range_badge'; const editPanelAction = { execute: jest.fn(), @@ -42,13 +39,7 @@ test(`badge is not compatible with embeddable that inherits from parent`, async const child = container.getChild('1'); - const compatible = await new CustomTimeRangeBadge( - overlayServiceMock.createStartContract(), - themeServiceMock.createStartContract(), - editPanelAction, - [], - 'MM YYYY' - ).isCompatible({ + const compatible = await new CustomTimeRangeBadge(editPanelAction, 'MM YYYY').isCompatible({ embeddable: child, }); expect(compatible).toBe(false); @@ -76,13 +67,7 @@ test(`badge is compatible with embeddable that has custom time range`, async () const child = container.getChild('1'); - const compatible = await new CustomTimeRangeBadge( - overlayServiceMock.createStartContract(), - themeServiceMock.createStartContract(), - editPanelAction, - [], - 'MM YYYY' - ).isCompatible({ + const compatible = await new CustomTimeRangeBadge(editPanelAction, 'MM YYYY').isCompatible({ embeddable: child, }); expect(compatible).toBe(true); @@ -109,13 +94,7 @@ test('Attempting to execute on incompatible embeddable throws an error', async ( const child = container.getChild('1'); - const badge = await new CustomTimeRangeBadge( - overlayServiceMock.createStartContract(), - themeServiceMock.createStartContract(), - editPanelAction, - [], - 'MM YYYY' - ); + const badge = await new CustomTimeRangeBadge(editPanelAction, 'MM YYYY'); async function check() { await badge.execute({ embeddable: child }); diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.tsx index 08e864b76b1ab..5866c3b5ec195 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/custom_time_range_badge.tsx @@ -11,9 +11,10 @@ import { PrettyDuration } from '@elastic/eui'; import { renderToString } from 'react-dom/server'; import { Action } from '@kbn/ui-actions-plugin/public'; -import { Embeddable } from '../../..'; +import { EditPanelAction, Embeddable } from '../../..'; import { doesInheritTimeRange } from './does_inherit_time_range'; -import { TimeRangeInput, hasTimeRange, CustomizePanelAction } from './customize_panel_action'; +import { CustomizePanelAction } from './customize_panel_action'; +import { hasTimeRange, TimeRangeInput } from './time_range_helpers'; export const CUSTOM_TIME_RANGE_BADGE = 'CUSTOM_TIME_RANGE_BADGE'; @@ -29,6 +30,13 @@ export class CustomTimeRangeBadge public readonly id = CUSTOM_TIME_RANGE_BADGE; public order = 7; + constructor( + protected readonly editPanel: EditPanelAction, + protected readonly dateFormat?: string + ) { + super(editPanel); + } + public getDisplayName({ embeddable }: TimeBadgeActionContext) { return renderToString( { }); test('execute should open flyout', async () => { - const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock); - const spy = jest.spyOn(overlays, 'openFlyout'); - await customizePanelAction.execute({ embeddable }); + const customizePanelAction = new CustomizePanelAction(editPanelActionMock); + const spy = jest.spyOn(openCustomizePanel, 'openCustomizePanelFlyout'); + await customizePanelAction.execute({ embeddable }); expect(spy).toHaveBeenCalled(); }); diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx index a7fa84aa54188..7f70ecf51acac 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_action.tsx @@ -6,51 +6,18 @@ * Side Public License, v 1. */ -import { OverlayStart, ThemeServiceStart } from '@kbn/core/public'; -import { TimeRange } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { toMountPoint } from '@kbn/react-kibana-mount'; -import { Action, IncompatibleActionError, Trigger } from '@kbn/ui-actions-plugin/public'; -import React from 'react'; +import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { - EditPanelAction, - Embeddable, - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '../../..'; -import { core } from '../../../kibana_services'; -import { CommonlyUsedRange, ViewMode } from '../../../lib/types'; -import { tracksOverlays } from '../track_overlays'; -import { CustomizePanelEditor } from './customize_panel_editor'; +import { EditPanelAction, Embeddable, IEmbeddable } from '../../..'; +import { ViewMode } from '../../../lib/types'; +import { openCustomizePanelFlyout } from './open_customize_panel'; +import { isTimeRangeCompatible, TimeRangeInput } from './time_range_helpers'; export const ACTION_CUSTOMIZE_PANEL = 'ACTION_CUSTOMIZE_PANEL'; -const VISUALIZE_EMBEDDABLE_TYPE = 'visualization'; - -type VisualizeEmbeddable = IEmbeddable<{ id: string }, EmbeddableOutput & { visTypeName: string }>; - -function isVisualizeEmbeddable( - embeddable: IEmbeddable | VisualizeEmbeddable -): embeddable is VisualizeEmbeddable { - return embeddable.type === VISUALIZE_EMBEDDABLE_TYPE; -} - -export interface TimeRangeInput extends EmbeddableInput { - timeRange: TimeRange; -} - -export function hasTimeRange( - embeddable: IEmbeddable | Embeddable -): embeddable is Embeddable { - return (embeddable as Embeddable).getInput().timeRange !== undefined; -} - export interface CustomizePanelActionContext { embeddable: IEmbeddable | Embeddable; - trigger?: Trigger; } export class CustomizePanelAction implements Action { @@ -58,35 +25,7 @@ export class CustomizePanelAction implements Action public id = ACTION_CUSTOMIZE_PANEL; public order = 40; - constructor( - protected readonly overlays: OverlayStart, - protected readonly theme: ThemeServiceStart, - protected readonly editPanel: EditPanelAction, - protected readonly commonlyUsedRanges?: CommonlyUsedRange[], - protected readonly dateFormat?: string - ) {} - - protected isTimeRangeCompatible({ embeddable }: CustomizePanelActionContext): boolean { - const isInputControl = - isVisualizeEmbeddable(embeddable) && - (embeddable as VisualizeEmbeddable).getOutput().visTypeName === 'input_control_vis'; - - const isMarkdown = - isVisualizeEmbeddable(embeddable) && - (embeddable as VisualizeEmbeddable).getOutput().visTypeName === 'markdown'; - - const isImage = embeddable.type === 'image'; - const isNavigation = embeddable.type === 'navigation'; - - return Boolean( - embeddable && - hasTimeRange(embeddable) && - !isInputControl && - !isMarkdown && - !isImage && - !isNavigation - ); - } + constructor(protected readonly editPanel: EditPanelAction) {} public getDisplayName({ embeddable }: CustomizePanelActionContext): string { return i18n.translate('embeddableApi.customizePanel.action.displayName', { @@ -101,56 +40,15 @@ export class CustomizePanelAction implements Action public async isCompatible({ embeddable }: CustomizePanelActionContext) { // It should be possible to customize just the time range in View mode return ( - embeddable.getInput().viewMode === ViewMode.EDIT || this.isTimeRangeCompatible({ embeddable }) + embeddable.getInput().viewMode === ViewMode.EDIT || isTimeRangeCompatible({ embeddable }) ); } - public async execute({ embeddable, trigger }: CustomizePanelActionContext) { + public async execute({ embeddable }: CustomizePanelActionContext) { const isCompatible = await this.isCompatible({ embeddable }); if (!isCompatible) { throw new IncompatibleActionError(); } - - // send the overlay ref to the root embeddable if it is capable of tracking overlays - const rootEmbeddable = embeddable.getRoot(); - const overlayTracker = tracksOverlays(rootEmbeddable) ? rootEmbeddable : undefined; - - const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ - uiSettings: core.uiSettings, - }); - - const onEdit = () => { - this.editPanel.execute({ embeddable }); - }; - - const handle = this.overlays.openFlyout( - toMountPoint( - - { - if (overlayTracker) overlayTracker.clearOverlays(); - handle.close(); - }} - onEdit={onEdit} - /> - , - { theme: this.theme, i18n: core.i18n } - ), - { - size: 's', - 'data-test-subj': 'customizePanel', - onClose: (overlayRef) => { - if (overlayTracker) overlayTracker.clearOverlays(); - overlayRef.close(); - }, - maxWidth: true, - } - ); - overlayTracker?.openOverlay(handle); + openCustomizePanelFlyout({ editPanel: this.editPanel, embeddable }); } } diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx index ead6e30062cf5..d6cccfe0c0684 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx @@ -8,8 +8,6 @@ import React from 'react'; -import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; -import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import '@testing-library/jest-dom'; import { render, screen, waitFor } from '@testing-library/react'; @@ -26,12 +24,10 @@ import { EditPanelAction } from '../edit_panel_action/edit_panel_action'; import { CustomizePanelAction } from './customize_panel_action'; import { CustomizePanelEditor } from './customize_panel_editor'; -const overlays = overlayServiceMock.createStartContract(); -const theme = themeServiceMock.createStartContract(); const editPanelActionMock = { execute: jest.fn() } as unknown as EditPanelAction; const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); -const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock); +const customizePanelAction = new CustomizePanelAction(editPanelActionMock); customizePanelAction.execute = jest.fn(); const DEFAULT_PANEL_TITLE = 'Panel title'; diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx index 6bd6350ef08d7..9893e016af08b 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.tsx @@ -37,9 +37,9 @@ import { ViewMode, } from '../../../lib'; import { canInheritTimeRange } from './can_inherit_time_range'; -import { TimeRangeInput } from './customize_panel_action'; import { doesInheritTimeRange } from './does_inherit_time_range'; import { FiltersDetails } from './filters_details'; +import { TimeRangeInput } from './time_range_helpers'; type PanelSettings = { title?: string; @@ -55,11 +55,11 @@ interface CustomizePanelProps { commonlyUsedRanges?: CommonlyUsedRange[]; onClose: () => void; onEdit: () => void; - titleFocus?: boolean; + focusOnTitle?: boolean; } export const CustomizePanelEditor = (props: CustomizePanelProps) => { - const { onClose, embeddable, dateFormat, timeRangeCompatible, onEdit, titleFocus } = props; + const { onClose, embeddable, dateFormat, timeRangeCompatible, onEdit, focusOnTitle } = props; const editMode = embeddable.getInput().viewMode === ViewMode.EDIT; const [hideTitle, setHideTitle] = useState(embeddable.getInput().hidePanelTitles); const [panelDescription, setPanelDescription] = useState( @@ -76,13 +76,13 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { ? (embeddable as Embeddable).getInput().timeRange : undefined ); - const inputRef = useRef(null); + const initialFocusRef = useRef(null); useEffect(() => { - if (titleFocus && inputRef.current) { - inputRef.current.focus(); + if (focusOnTitle && initialFocusRef.current) { + initialFocusRef.current.focus(); } - }, [inputRef, titleFocus]); + }, [initialFocusRef, focusOnTitle]); const commonlyUsedRangesForDatePicker = props.commonlyUsedRanges ? props.commonlyUsedRanges.map( @@ -162,7 +162,7 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => { } > ; +}) => { + // send the overlay ref to the root embeddable if it is capable of tracking overlays + const rootEmbeddable = embeddable.getRoot(); + const overlayTracker = tracksOverlays(rootEmbeddable) ? rootEmbeddable : undefined; + + const commonlyUsedRanges = core.uiSettings.get(UI_SETTINGS.TIMEPICKER_QUICK_RANGES); + const dateFormat = core.uiSettings.get(UI_SETTINGS.DATE_FORMAT); + + const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + uiSettings: core.uiSettings, + }); + + const onEdit = () => { + editPanel.execute({ embeddable }); + }; + + const handle = core.overlays.openFlyout( + toMountPoint( + + { + if (overlayTracker) overlayTracker.clearOverlays(); + handle.close(); + }} + onEdit={onEdit} + /> + , + { theme: core.theme, i18n: core.i18n } + ), + { + size: 's', + 'data-test-subj': 'customizePanel', + onClose: (overlayRef) => { + if (overlayTracker) overlayTracker.clearOverlays(); + overlayRef.close(); + }, + maxWidth: true, + } + ); + overlayTracker?.openOverlay(handle); +}; diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/time_range_helpers.ts b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/time_range_helpers.ts new file mode 100644 index 0000000000000..0b74809be8a08 --- /dev/null +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/time_range_helpers.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TimeRange } from '@kbn/es-query'; + +import { Embeddable, EmbeddableInput, EmbeddableOutput, IEmbeddable } from '../../../lib'; + +const VISUALIZE_EMBEDDABLE_TYPE = 'visualization'; + +type VisualizeEmbeddable = IEmbeddable<{ id: string }, EmbeddableOutput & { visTypeName: string }>; + +function isVisualizeEmbeddable( + embeddable: IEmbeddable | VisualizeEmbeddable +): embeddable is VisualizeEmbeddable { + return embeddable.type === VISUALIZE_EMBEDDABLE_TYPE; +} + +export interface TimeRangeInput extends EmbeddableInput { + timeRange: TimeRange; +} + +export function hasTimeRange( + embeddable: IEmbeddable | Embeddable +): embeddable is Embeddable { + return (embeddable as Embeddable).getInput().timeRange !== undefined; +} + +export function isTimeRangeCompatible({ embeddable }: { embeddable: IEmbeddable }): boolean { + const isInputControl = + isVisualizeEmbeddable(embeddable) && + (embeddable as VisualizeEmbeddable).getOutput().visTypeName === 'input_control_vis'; + + const isMarkdown = + isVisualizeEmbeddable(embeddable) && + (embeddable as VisualizeEmbeddable).getOutput().visTypeName === 'markdown'; + + const isImage = embeddable.type === 'image'; + const isNavigation = embeddable.type === 'navigation'; + + return Boolean( + embeddable && + hasTimeRange(embeddable) && + !isInputControl && + !isMarkdown && + !isImage && + !isNavigation + ); +} diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx index 759e8d35a0726..3de27d6ad193c 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.tsx @@ -104,7 +104,7 @@ export const EmbeddablePanelHeader = ({ hideTitle={hideTitle} embeddable={embeddable} description={description} - customizePanelAction={universalActions.customizePanel} + editPanelAction={universalActions.editPanel} /> {showBadges && badgeComponents} diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx index 4341f5aa4a983..693215a3084ca 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_title.tsx @@ -11,21 +11,22 @@ import React, { useMemo } from 'react'; import { EuiIcon, EuiLink, EuiToolTip } from '@elastic/eui'; import { IEmbeddable, ViewMode } from '../../lib'; -import { CustomizePanelAction } from '../panel_actions'; import { getEditTitleAriaLabel, placeholderTitle } from '../embeddable_panel_strings'; +import { EditPanelAction } from '../panel_actions'; +import { openCustomizePanelFlyout } from '../panel_actions/customize_panel_action/open_customize_panel'; export const EmbeddablePanelTitle = ({ viewMode, hideTitle, embeddable, description, - customizePanelAction, + editPanelAction, }: { hideTitle?: boolean; viewMode?: ViewMode; description?: string; embeddable: IEmbeddable; - customizePanelAction?: CustomizePanelAction; + editPanelAction?: EditPanelAction; }) => { const title = embeddable.getTitle(); @@ -39,21 +40,27 @@ export const EmbeddablePanelTitle = ({ if (viewMode === ViewMode.VIEW) { return {title}; } - if (customizePanelAction) { + if (editPanelAction) { return ( customizePanelAction.execute({ embeddable })} + onClick={() => + openCustomizePanelFlyout({ + editPanel: editPanelAction, + embeddable, + focusOnTitle: true, + }) + } > {title || placeholderTitle} ); } return null; - }, [customizePanelAction, embeddable, title, viewMode, hideTitle]); + }, [editPanelAction, embeddable, title, viewMode, hideTitle]); const titleComponentWithDescription = useMemo(() => { if (!description) diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index d7af8f68817ea..5eb6473ddb930 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -146,10 +146,9 @@ export class EmbeddablePublicPlugin implements Plugin { this.appList = appList; @@ -168,13 +167,7 @@ export class EmbeddablePublicPlugin implements Plugin Date: Thu, 4 Jan 2024 11:17:08 -0700 Subject: [PATCH 6/7] Fix types + failing test --- .../can_inherit_time_range.ts | 2 +- .../customize_panel_editor.test.tsx | 8 ++--- .../does_inherit_time_range.ts | 4 +-- .../embeddable_panel_header.test.tsx | 33 +++++++++---------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/can_inherit_time_range.ts b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/can_inherit_time_range.ts index 139933c8d9390..f2c1a3b7a9aac 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/can_inherit_time_range.ts +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/can_inherit_time_range.ts @@ -8,8 +8,8 @@ import type { TimeRange } from '@kbn/es-query'; -import { TimeRangeInput } from './customize_panel_action'; import { Embeddable, IContainer, ContainerInput } from '../../..'; +import { TimeRangeInput } from './time_range_helpers'; interface ContainerTimeRangeInput extends ContainerInput { timeRange: TimeRange; diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx index d6cccfe0c0684..a2ccf9782deba 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor.test.tsx @@ -64,11 +64,11 @@ describe('panel title / description', () => { expect(customizePanelForm).not.toContainElement(titleDescriptionComponent); }); - test('title input receives focus when `titleFocus` is `true`', async () => { + test('title input receives focus when `focusOnTitle` is `true`', async () => { const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); render( - + ); @@ -76,11 +76,11 @@ describe('panel title / description', () => { expect(customTitleComponent).toHaveFocus(); }); - test('title input does not receive focus when `titleFocus` is `false`', async () => { + test('title input does not receive focus when `focusOnTitle` is `false`', async () => { const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); render( - + ); diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/does_inherit_time_range.ts b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/does_inherit_time_range.ts index a14ca031c9fb1..7d21a79ab9425 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/does_inherit_time_range.ts +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/customize_panel_action/does_inherit_time_range.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { Embeddable, IContainer, ContainerInput } from '../../../lib'; -import { TimeRangeInput } from './customize_panel_action'; +import { ContainerInput, Embeddable, IContainer } from '../../../lib'; +import { TimeRangeInput } from './time_range_helpers'; export function doesInheritTimeRange(embeddable: Embeddable) { if (!embeddable.parent) { diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.test.tsx b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.test.tsx index b070837ce7ad9..770ca76edbc33 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.test.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/panel_header/embeddable_panel_header.test.tsx @@ -8,13 +8,10 @@ import React from 'react'; -import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks'; -import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; -// import userEvent from '@testing-library/user-event'; -// import { ViewMode } from '../../lib'; +import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; import userEvent from '@testing-library/user-event'; import { ViewMode } from '../../../common'; import { @@ -22,16 +19,16 @@ import { ContactCardEmbeddableFactory, ContactCardEmbeddableInput, } from '../../lib/test_samples'; -import { CustomizePanelAction, EditPanelAction } from '../panel_actions'; +import { EditPanelAction } from '../panel_actions'; +import * as openCustomizePanel from '../panel_actions/customize_panel_action/open_customize_panel'; import { EmbeddablePanelHeader } from './embeddable_panel_header'; -const overlays = overlayServiceMock.createStartContract(); -const theme = themeServiceMock.createStartContract(); -const editPanelActionMock = { execute: jest.fn() } as unknown as EditPanelAction; +const getEmbeddableFactory = jest.fn(); +const application = applicationServiceMock.createStartContract(); +const editPanelAction = new EditPanelAction(getEmbeddableFactory, application); const mockEmbeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); -const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock); -customizePanelAction.execute = jest.fn(); +editPanelAction.execute = jest.fn(); const DEFAULT_PANEL_TITLE = 'Panel title'; @@ -130,10 +127,10 @@ describe('edit mode', () => { ); - const titleComponent = await screen.findByTestId('dashboardPanelTitle'); + const titleComponent = await screen.findByTestId('embeddablePanelTitleInner'); expect(titleComponent).toHaveTextContent('[No Title]'); }); @@ -146,10 +143,9 @@ describe('edit mode', () => { ); - screen.debug(); const titleComponent = await screen.findByTestId('embeddablePanelHeading-'); const innerTitleComponent = await screen.findByTestId('embeddablePanelTitleInner'); expect(innerTitleComponent).toBeEmptyDOMElement(); @@ -157,18 +153,19 @@ describe('edit mode', () => { expect(titleComponent).toContainElement(menuComponent); }); - test('clicking title calls customize panel action', async () => { + test('clicking title opens customize panel flyout', async () => { const mockEmbeddable = await createEmbeddable({ viewMode: ViewMode.EDIT }); render( ); - screen.debug(); const titleComponent = await screen.findByTestId('embeddablePanelTitleLink'); + + const spy = jest.spyOn(openCustomizePanel, 'openCustomizePanelFlyout'); userEvent.click(titleComponent); - expect(customizePanelAction.execute).toBeCalled(); + expect(spy).toHaveBeenCalled(); }); }); From 4d53b615bf4bfab88ea6b8e3906977988f21ebb4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 4 Jan 2024 18:24:58 +0000 Subject: [PATCH 7/7] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/embeddable/tsconfig.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/embeddable/tsconfig.json b/src/plugins/embeddable/tsconfig.json index 63f15043bfd7d..bc921c9f160f8 100644 --- a/src/plugins/embeddable/tsconfig.json +++ b/src/plugins/embeddable/tsconfig.json @@ -21,8 +21,6 @@ "@kbn/std", "@kbn/expressions-plugin", "@kbn/data-plugin", - "@kbn/core-overlays-browser-mocks", - "@kbn/core-theme-browser-mocks", "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-tagging-oss-plugin", "@kbn/saved-objects-finder-plugin", @@ -36,6 +34,7 @@ "@kbn/data-views-plugin", "@kbn/search-errors", "@kbn/panel-loader", + "@kbn/core-application-browser-mocks", ], "exclude": ["target/**/*"] }