diff --git a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.test.tsx index 30532a50e5cb3..20af7a59758b0 100644 --- a/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/embeddable_panel/embeddable_panel.test.tsx @@ -31,6 +31,10 @@ import { EmbeddablePanel } from './embeddable_panel'; import { core, inspector } from '../kibana_services'; import { CONTEXT_MENU_TRIGGER, ViewMode } from '..'; import { UnwrappedEmbeddablePanelProps } from './types'; +import { + DESCRIPTIVE_CONTACT_CARD_EMBEDDABLE, + DescriptiveContactCardEmbeddableFactory, +} from '../lib/test_samples/embeddables/contact_card/descriptive_contact_card_embeddable_factory'; const actionRegistry = new Map(); const triggerRegistry = new Map(); @@ -46,11 +50,15 @@ const embeddableReactFactory = new ContactCardEmbeddableReactFactory( (() => null) as any, {} as any ); +const descriptiveEmbeddableFactory = new DescriptiveContactCardEmbeddableFactory( + (() => null) as any +); actionRegistry.set(editModeAction.id, new ActionInternal(editModeAction)); triggerRegistry.set(trigger.id, trigger); setup.registerEmbeddableFactory(embeddableFactory.type, embeddableFactory); setup.registerEmbeddableFactory(embeddableReactFactory.type, embeddableReactFactory); +setup.registerEmbeddableFactory(descriptiveEmbeddableFactory.type, descriptiveEmbeddableFactory); const start = doStart(); const getEmbeddableFactory = start.getEmbeddableFactory; @@ -69,7 +77,11 @@ const renderEmbeddableInPanel = async ( return wrapper!; }; -const setupContainerAndEmbeddable = async (viewMode?: ViewMode, hidePanelTitles?: boolean) => { +const setupContainerAndEmbeddable = async ( + embeddableType: string, + viewMode: ViewMode = ViewMode.VIEW, + hidePanelTitles?: boolean +) => { const container = new HelloWorldContainer( { id: '123', panels: {}, viewMode: viewMode ?? ViewMode.VIEW, hidePanelTitles }, { @@ -81,7 +93,7 @@ const setupContainerAndEmbeddable = async (viewMode?: ViewMode, hidePanelTitles? ContactCardEmbeddableInput, ContactCardEmbeddableOutput, ContactCardEmbeddable - >(CONTACT_CARD_EMBEDDABLE, { + >(embeddableType, { firstName: 'Jack', lastName: 'Orange', }); @@ -250,7 +262,7 @@ describe('Error states', () => { }); test('Render method is called on Embeddable', async () => { - const { embeddable } = await setupContainerAndEmbeddable(); + const { embeddable } = await setupContainerAndEmbeddable(CONTACT_CARD_EMBEDDABLE); jest.spyOn(embeddable, 'render'); await renderEmbeddableInPanel({ embeddable }); expect(embeddable.render).toHaveBeenCalledTimes(1); @@ -379,7 +391,7 @@ test('Notifications are not shown when hideNotifications is true', async () => { }); test('Edit mode actions are hidden if parent is in view mode', async () => { - const { embeddable } = await setupContainerAndEmbeddable(); + const { embeddable } = await setupContainerAndEmbeddable(CONTACT_CARD_EMBEDDABLE); const component = await renderEmbeddableInPanel({ embeddable }); @@ -395,7 +407,7 @@ test('Edit mode actions are hidden if parent is in view mode', async () => { }); test('Edit mode actions are shown in edit mode', async () => { - const { container, embeddable } = await setupContainerAndEmbeddable(); + const { container, embeddable } = await setupContainerAndEmbeddable(CONTACT_CARD_EMBEDDABLE); const component = await renderEmbeddableInPanel({ embeddable }); @@ -442,7 +454,11 @@ test('Edit mode actions are shown in edit mode', async () => { }); test('Panel title customize link does not exist in view mode', async () => { - const { embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, false); + const { embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + false + ); const component = await renderEmbeddableInPanel({ embeddable }); @@ -454,7 +470,11 @@ test('Runs customize panel action on title click when in edit mode', async () => // spy on core openFlyout to check that the flyout is opened correctly. core.overlays.openFlyout = jest.fn(); - const { embeddable } = await setupContainerAndEmbeddable(ViewMode.EDIT, false); + const { embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.EDIT, + false + ); const component = await renderEmbeddableInPanel({ embeddable }); @@ -472,7 +492,16 @@ test('Runs customize panel action on title click when in edit mode', async () => }); test('Updates when hidePanelTitles is toggled', async () => { - const { container, embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, false); + const { container, embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + false + ); + /** + * panel title will always show if a description is set so we explictily set the panel + * description so the embeddable description is not used + */ + embeddable.updateInput({ description: '' }); const component = await renderEmbeddableInPanel({ embeddable }); await component.update(); @@ -499,7 +528,11 @@ test('Updates when hidePanelTitles is toggled', async () => { }); test('Respects options from SelfStyledEmbeddable', async () => { - const { container, embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, false); + const { container, embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + false + ); const selfStyledEmbeddable = embeddablePluginMock.mockSelfStyledEmbeddable(embeddable, { hideTitle: true, @@ -514,8 +547,24 @@ test('Respects options from SelfStyledEmbeddable', async () => { expect(title.length).toBe(0); }); +test('Shows icon in panel title when the embeddable has a description', async () => { + const { embeddable } = await setupContainerAndEmbeddable( + DESCRIPTIVE_CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + false + ); + const component = await renderEmbeddableInPanel({ embeddable }); + + const descriptionIcon = findTestSubject(component, 'embeddablePanelTitleDescriptionIcon'); + expect(descriptionIcon.length).toBe(1); +}); + test('Does not hide header when parent hide header option is false', async () => { - const { embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, false); + const { embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + false + ); const component = await renderEmbeddableInPanel({ embeddable }); @@ -524,7 +573,11 @@ test('Does not hide header when parent hide header option is false', async () => }); test('Hides title when parent hide header option is true', async () => { - const { embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, true); + const { embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + true + ); const component = await renderEmbeddableInPanel({ embeddable }); @@ -535,7 +588,11 @@ test('Hides title when parent hide header option is true', async () => { test('Should work in minimal way rendering only the inspector action', async () => { inspector.isAvailable = jest.fn(() => true); - const { embeddable } = await setupContainerAndEmbeddable(ViewMode.VIEW, true); + const { embeddable } = await setupContainerAndEmbeddable( + CONTACT_CARD_EMBEDDABLE, + ViewMode.VIEW, + true + ); const component = await renderEmbeddableInPanel({ embeddable }); 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 adefe08248e87..ee27bbca0605a 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 @@ -44,8 +44,8 @@ export const EmbeddablePanelHeader = ({ ); const title = embeddable.getTitle(); + const description = embeddable.getDescription(); const viewMode = useSelectFromEmbeddableInput('viewMode', embeddable); - const description = useSelectFromEmbeddableInput('description', embeddable); const hidePanelTitle = useSelectFromEmbeddableInput('hidePanelTitles', embeddable); const parentHidePanelTitle = useSelectFromEmbeddableInput('hidePanelTitles', embeddable.parent); 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 968aa11de9c38..734b420d04052 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 @@ -65,7 +65,12 @@ export const EmbeddablePanelTitle = ({ anchorClassName="embPanel__titleTooltipAnchor" > - {titleComponent} + {titleComponent}{' '} + ); diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx index 0287b9d115827..9a8c11ecd266b 100644 --- a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/contact_card_embeddable.tsx @@ -29,6 +29,7 @@ export interface ContactCardEmbeddableOutput extends EmbeddableOutput { export interface ContactCardEmbeddableOptions { execAction: UiActionsStart['executeTriggerActions']; + outputOverrides?: Partial; } function getFullName(input: ContactCardEmbeddableInput) { @@ -56,6 +57,7 @@ export class ContactCardEmbeddable extends Embeddable< fullName: getFullName(initialInput), originalLastName: initialInput.lastName, defaultTitle: `Hello ${getFullName(initialInput)}`, + ...options.outputOverrides, }, parent ); diff --git a/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/descriptive_contact_card_embeddable_factory.ts b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/descriptive_contact_card_embeddable_factory.ts new file mode 100644 index 0000000000000..046f14d060610 --- /dev/null +++ b/src/plugins/embeddable/public/lib/test_samples/embeddables/contact_card/descriptive_contact_card_embeddable_factory.ts @@ -0,0 +1,43 @@ +/* + * 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 { UiActionsStart } from '@kbn/ui-actions-plugin/public'; + +import { Container, EmbeddableFactoryDefinition } from '../../..'; +import { ContactCardEmbeddable, ContactCardEmbeddableInput } from './contact_card_embeddable'; + +export const DESCRIPTIVE_CONTACT_CARD_EMBEDDABLE = 'DESCRIPTIVE_CONTACT_CARD_EMBEDDABLE'; + +export class DescriptiveContactCardEmbeddableFactory + implements EmbeddableFactoryDefinition +{ + public readonly type = DESCRIPTIVE_CONTACT_CARD_EMBEDDABLE; + + constructor(protected readonly execTrigger: UiActionsStart['executeTriggerActions']) {} + + public async isEditable() { + return true; + } + + public getDisplayName() { + return 'descriptive contact card'; + } + + public create = async (initialInput: ContactCardEmbeddableInput, parent?: Container) => { + return new ContactCardEmbeddable( + initialInput, + { + execAction: this.execTrigger, + outputOverrides: { + defaultDescription: 'This is a family friend', + }, + }, + parent + ); + }; +}