mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* Clone all panels by value.
* Moved removal of byReference properties to getInputAsValueType.
* Fixed handling of clone titles.
* Fixed functional and unit clone tests.
* Removed duplicate check for byReference.
* Unset title on Visualize embeddable when by value.
* Remove unused import.
* Added by reference logic for saved search embeddables.
* Re-added unit tests for cloning by reference.
* Added functional tests.
* Added Jest unit tests.
* Ignored TypeScript errors for calling private functions in Jest tests.
* Adjusted logic for generating clone titles.
* Edited unit and functional tests for clone titles.
* Fixed typo in Jest tests.
* Keep hidden panel title status.
* Fix Jest test description.
* Remove unused import.
* Fixed Jest tests after new title logic.
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit b1d7731afa
)
Co-authored-by: Hannah Mudge <Heenawter@users.noreply.github.com>
This commit is contained in:
parent
24aa77df59
commit
5407543a57
7 changed files with 286 additions and 77 deletions
|
@ -33,7 +33,8 @@ setup.registerEmbeddableFactory(
|
|||
const start = doStart();
|
||||
|
||||
let container: DashboardContainer;
|
||||
let embeddable: ContactCardEmbeddable;
|
||||
let byRefOrValEmbeddable: ContactCardEmbeddable;
|
||||
let genericEmbeddable: ContactCardEmbeddable;
|
||||
let coreStart: CoreStart;
|
||||
beforeEach(async () => {
|
||||
coreStart = coreMock.createStart();
|
||||
|
@ -69,18 +70,38 @@ beforeEach(async () => {
|
|||
});
|
||||
container = new DashboardContainer(input, options);
|
||||
|
||||
const contactCardEmbeddable = await container.addNewEmbeddable<
|
||||
const refOrValContactCardEmbeddable = await container.addNewEmbeddable<
|
||||
ContactCardEmbeddableInput,
|
||||
ContactCardEmbeddableOutput,
|
||||
ContactCardEmbeddable
|
||||
>(CONTACT_CARD_EMBEDDABLE, {
|
||||
firstName: 'Kibana',
|
||||
firstName: 'RefOrValEmbeddable',
|
||||
});
|
||||
const genericContactCardEmbeddable = await container.addNewEmbeddable<
|
||||
ContactCardEmbeddableInput,
|
||||
ContactCardEmbeddableOutput,
|
||||
ContactCardEmbeddable
|
||||
>(CONTACT_CARD_EMBEDDABLE, {
|
||||
firstName: 'NotRefOrValEmbeddable',
|
||||
});
|
||||
|
||||
if (isErrorEmbeddable(contactCardEmbeddable)) {
|
||||
throw new Error('Failed to create embeddable');
|
||||
if (
|
||||
isErrorEmbeddable(refOrValContactCardEmbeddable) ||
|
||||
isErrorEmbeddable(genericContactCardEmbeddable)
|
||||
) {
|
||||
throw new Error('Failed to create embeddables');
|
||||
} else {
|
||||
embeddable = contactCardEmbeddable;
|
||||
byRefOrValEmbeddable = embeddablePluginMock.mockRefOrValEmbeddable<
|
||||
ContactCardEmbeddable,
|
||||
ContactCardEmbeddableInput
|
||||
>(refOrValContactCardEmbeddable, {
|
||||
mockedByReferenceInput: {
|
||||
savedObjectId: 'testSavedObjectId',
|
||||
id: refOrValContactCardEmbeddable.id,
|
||||
},
|
||||
mockedByValueInput: { firstName: 'Kibanana', id: refOrValContactCardEmbeddable.id },
|
||||
});
|
||||
genericEmbeddable = genericContactCardEmbeddable;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -89,17 +110,17 @@ test('Clone is incompatible with Error Embeddables', async () => {
|
|||
const errorEmbeddable = new ErrorEmbeddable(
|
||||
'Wow what an awful error',
|
||||
{ id: ' 404' },
|
||||
embeddable.getRoot() as IContainer
|
||||
byRefOrValEmbeddable.getRoot() as IContainer
|
||||
);
|
||||
expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false);
|
||||
});
|
||||
|
||||
test('Clone adds a new embeddable', async () => {
|
||||
const dashboard = embeddable.getRoot() as IContainer;
|
||||
const dashboard = byRefOrValEmbeddable.getRoot() as IContainer;
|
||||
const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
|
||||
const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
await action.execute({ embeddable });
|
||||
await action.execute({ embeddable: byRefOrValEmbeddable });
|
||||
expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount + 1);
|
||||
const newPanelId = Object.keys(container.getInput().panels).find(
|
||||
(key) => !originalPanelKeySet.has(key)
|
||||
|
@ -112,56 +133,159 @@ test('Clone adds a new embeddable', async () => {
|
|||
await new Promise((r) => process.nextTick(r)); // Allow the current loop of the event loop to run to completion
|
||||
// now wait for the full embeddable to replace it
|
||||
const loadedPanel = await dashboard.untilEmbeddableLoaded(newPanelId!);
|
||||
expect(loadedPanel.type).toEqual(embeddable.type);
|
||||
expect(loadedPanel.type).toEqual(byRefOrValEmbeddable.type);
|
||||
});
|
||||
|
||||
test('Clones an embeddable without a saved object ID', async () => {
|
||||
const dashboard = embeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
|
||||
test('Clones a RefOrVal embeddable by value', async () => {
|
||||
const dashboard = byRefOrValEmbeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[byRefOrValEmbeddable.id] as DashboardPanelState;
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
// @ts-ignore
|
||||
const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
|
||||
expect(newPanel.type).toEqual(embeddable.type);
|
||||
const newPanel = await action.cloneEmbeddable(panel, byRefOrValEmbeddable);
|
||||
expect(coreStart.savedObjects.client.get).toHaveBeenCalledTimes(0);
|
||||
expect(coreStart.savedObjects.client.find).toHaveBeenCalledTimes(0);
|
||||
expect(coreStart.savedObjects.client.create).toHaveBeenCalledTimes(0);
|
||||
expect(newPanel.type).toEqual(byRefOrValEmbeddable.type);
|
||||
});
|
||||
|
||||
test('Clones an embeddable with a saved object ID', async () => {
|
||||
const dashboard = embeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
|
||||
test('Clones a non-RefOrVal embeddable by value if the panel does not have a savedObjectId', async () => {
|
||||
const dashboard = genericEmbeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[genericEmbeddable.id] as DashboardPanelState;
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
// @ts-ignore
|
||||
const newPanelWithoutId = await action.cloneEmbeddable(panel, genericEmbeddable);
|
||||
expect(coreStart.savedObjects.client.get).toHaveBeenCalledTimes(0);
|
||||
expect(coreStart.savedObjects.client.find).toHaveBeenCalledTimes(0);
|
||||
expect(coreStart.savedObjects.client.create).toHaveBeenCalledTimes(0);
|
||||
expect(newPanelWithoutId.type).toEqual(genericEmbeddable.type);
|
||||
});
|
||||
|
||||
test('Clones a non-RefOrVal embeddable by reference if the panel has a savedObjectId', async () => {
|
||||
const dashboard = genericEmbeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[genericEmbeddable.id] as DashboardPanelState;
|
||||
panel.explicitInput.savedObjectId = 'holySavedObjectBatman';
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
// @ts-ignore
|
||||
const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
|
||||
const newPanel = await action.cloneEmbeddable(panel, genericEmbeddable);
|
||||
expect(coreStart.savedObjects.client.get).toHaveBeenCalledTimes(1);
|
||||
expect(coreStart.savedObjects.client.find).toHaveBeenCalledTimes(1);
|
||||
expect(coreStart.savedObjects.client.create).toHaveBeenCalledTimes(1);
|
||||
expect(newPanel.type).toEqual(embeddable.type);
|
||||
expect(newPanel.type).toEqual(genericEmbeddable.type);
|
||||
});
|
||||
|
||||
test('Gets a unique title ', async () => {
|
||||
test('Gets a unique title from the saved objects library', async () => {
|
||||
const dashboard = genericEmbeddable.getRoot() as IContainer;
|
||||
const panel = dashboard.getInput().panels[genericEmbeddable.id] as DashboardPanelState;
|
||||
panel.explicitInput.savedObjectId = 'holySavedObjectBatman';
|
||||
coreStart.savedObjects.client.find = jest.fn().mockImplementation(({ search }) => {
|
||||
if (search === '"testFirstTitle"') return { total: 1 };
|
||||
else if (search === '"testSecondTitle"') return { total: 41 };
|
||||
else if (search === '"testThirdTitle"') return { total: 90 };
|
||||
if (search === '"testFirstClone"') {
|
||||
return {
|
||||
savedObjects: [
|
||||
{
|
||||
attributes: { title: 'testFirstClone' },
|
||||
get: jest.fn().mockReturnValue('testFirstClone'),
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
};
|
||||
} else if (search === '"testBeforePageLimit"') {
|
||||
return {
|
||||
savedObjects: [
|
||||
{
|
||||
attributes: { title: 'testBeforePageLimit (copy 9)' },
|
||||
get: jest.fn().mockReturnValue('testBeforePageLimit (copy 9)'),
|
||||
},
|
||||
],
|
||||
total: 10,
|
||||
};
|
||||
} else if (search === '"testMaxLogic"') {
|
||||
return {
|
||||
savedObjects: [
|
||||
{
|
||||
attributes: { title: 'testMaxLogic (copy 10000)' },
|
||||
get: jest.fn().mockReturnValue('testMaxLogic (copy 10000)'),
|
||||
},
|
||||
],
|
||||
total: 2,
|
||||
};
|
||||
} else if (search === '"testAfterPageLimit"') {
|
||||
return { total: 11 };
|
||||
}
|
||||
});
|
||||
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
// @ts-ignore
|
||||
expect(await action.getUniqueTitle('testFirstTitle', embeddable.type)).toEqual(
|
||||
'testFirstTitle (copy)'
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testFirstClone')).toEqual(
|
||||
'testFirstClone (copy)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getUniqueTitle('testSecondTitle (copy 39)', embeddable.type)).toEqual(
|
||||
'testSecondTitle (copy 40)'
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testBeforePageLimit')).toEqual(
|
||||
'testBeforePageLimit (copy 10)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getUniqueTitle('testSecondTitle (copy 20)', embeddable.type)).toEqual(
|
||||
'testSecondTitle (copy 40)'
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testBeforePageLimit (copy 9)')).toEqual(
|
||||
'testBeforePageLimit (copy 10)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getUniqueTitle('testThirdTitle', embeddable.type)).toEqual(
|
||||
'testThirdTitle (copy 89)'
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testMaxLogic')).toEqual(
|
||||
'testMaxLogic (copy 10001)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getUniqueTitle('testThirdTitle (copy 10000)', embeddable.type)).toEqual(
|
||||
'testThirdTitle (copy 89)'
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testAfterPageLimit')).toEqual(
|
||||
'testAfterPageLimit (copy 11)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testAfterPageLimit (copy 10)')).toEqual(
|
||||
'testAfterPageLimit (copy 11)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(genericEmbeddable, 'testAfterPageLimit (copy 10000)')).toEqual(
|
||||
'testAfterPageLimit (copy 11)'
|
||||
);
|
||||
});
|
||||
|
||||
test('Gets a unique title from the dashboard', async () => {
|
||||
const dashboard = genericEmbeddable.getRoot() as DashboardContainer;
|
||||
const action = new ClonePanelAction(coreStart);
|
||||
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, '')).toEqual('');
|
||||
|
||||
dashboard.getPanelTitles = jest.fn().mockImplementation(() => {
|
||||
return ['testDuplicateTitle', 'testDuplicateTitle (copy)', 'testUniqueTitle'];
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testUniqueTitle')).toEqual(
|
||||
'testUniqueTitle (copy)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testDuplicateTitle')).toEqual(
|
||||
'testDuplicateTitle (copy 1)'
|
||||
);
|
||||
|
||||
dashboard.getPanelTitles = jest.fn().mockImplementation(() => {
|
||||
return ['testDuplicateTitle', 'testDuplicateTitle (copy)'].concat(
|
||||
Array.from([...Array(39)], (_, index) => `testDuplicateTitle (copy ${index + 1})`)
|
||||
);
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testDuplicateTitle')).toEqual(
|
||||
'testDuplicateTitle (copy 40)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testDuplicateTitle (copy 100)')).toEqual(
|
||||
'testDuplicateTitle (copy 40)'
|
||||
);
|
||||
|
||||
dashboard.getPanelTitles = jest.fn().mockImplementation(() => {
|
||||
return ['testDuplicateTitle (copy 100)'];
|
||||
});
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testDuplicateTitle')).toEqual(
|
||||
'testDuplicateTitle (copy 101)'
|
||||
);
|
||||
// @ts-ignore
|
||||
expect(await action.getCloneTitle(byRefOrValEmbeddable, 'testDuplicateTitle (copy 100)')).toEqual(
|
||||
'testDuplicateTitle (copy 101)'
|
||||
);
|
||||
});
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
EmbeddableInput,
|
||||
SavedObjectEmbeddableInput,
|
||||
isErrorEmbeddable,
|
||||
isReferenceOrValueEmbeddable,
|
||||
} from '../../services/embeddable';
|
||||
import {
|
||||
placePanelBeside,
|
||||
|
@ -78,7 +79,7 @@ export class ClonePanelAction implements Action<ClonePanelActionContext> {
|
|||
}
|
||||
|
||||
dashboard.showPlaceholderUntil(
|
||||
this.cloneEmbeddable(panelToClone, embeddable.type),
|
||||
this.cloneEmbeddable(panelToClone, embeddable),
|
||||
placePanelBeside,
|
||||
{
|
||||
width: panelToClone.gridData.w,
|
||||
|
@ -89,56 +90,106 @@ export class ClonePanelAction implements Action<ClonePanelActionContext> {
|
|||
);
|
||||
}
|
||||
|
||||
private async getUniqueTitle(rawTitle: string, embeddableType: string): Promise<string> {
|
||||
private async getCloneTitle(embeddable: IEmbeddable, rawTitle: string) {
|
||||
if (rawTitle === '') return ''; // If
|
||||
|
||||
const clonedTag = dashboardClonePanelAction.getClonedTag();
|
||||
const cloneRegex = new RegExp(`\\(${clonedTag}\\)`, 'g');
|
||||
const cloneNumberRegex = new RegExp(`\\(${clonedTag} [0-9]+\\)`, 'g');
|
||||
const baseTitle = rawTitle.replace(cloneNumberRegex, '').replace(cloneRegex, '').trim();
|
||||
let similarTitles: string[];
|
||||
if (
|
||||
isReferenceOrValueEmbeddable(embeddable) ||
|
||||
!_.has(embeddable.getExplicitInput(), 'savedObjectId')
|
||||
) {
|
||||
const dashboard: DashboardContainer = embeddable.getRoot() as DashboardContainer;
|
||||
similarTitles = _.filter(await dashboard.getPanelTitles(), (title: string) => {
|
||||
return title.startsWith(baseTitle);
|
||||
});
|
||||
} else {
|
||||
const perPage = 10;
|
||||
const similarSavedObjects = await this.core.savedObjects.client.find<SavedObject>({
|
||||
type: embeddable.type,
|
||||
perPage,
|
||||
fields: ['title'],
|
||||
searchFields: ['title'],
|
||||
search: `"${baseTitle}"`,
|
||||
});
|
||||
if (similarSavedObjects.total <= perPage) {
|
||||
similarTitles = similarSavedObjects.savedObjects.map((savedObject) => {
|
||||
return savedObject.get('title');
|
||||
});
|
||||
} else {
|
||||
similarTitles = [baseTitle + ` (${clonedTag} ${similarSavedObjects.total - 1})`];
|
||||
}
|
||||
}
|
||||
|
||||
const similarSavedObjects = await this.core.savedObjects.client.find<SavedObject>({
|
||||
type: embeddableType,
|
||||
perPage: 0,
|
||||
fields: ['title'],
|
||||
searchFields: ['title'],
|
||||
search: `"${baseTitle}"`,
|
||||
const cloneNumbers = _.map(similarTitles, (title: string) => {
|
||||
if (title.match(cloneRegex)) return 0;
|
||||
const cloneTag = title.match(cloneNumberRegex);
|
||||
return cloneTag ? parseInt(cloneTag[0].replace(/[^0-9.]/g, ''), 10) : -1;
|
||||
});
|
||||
const similarBaseTitlesCount: number = similarSavedObjects.total - 1;
|
||||
const similarBaseTitlesCount = _.max(cloneNumbers) || 0;
|
||||
|
||||
return similarBaseTitlesCount <= 0
|
||||
return similarBaseTitlesCount < 0
|
||||
? baseTitle + ` (${clonedTag})`
|
||||
: baseTitle + ` (${clonedTag} ${similarBaseTitlesCount})`;
|
||||
: baseTitle + ` (${clonedTag} ${similarBaseTitlesCount + 1})`;
|
||||
}
|
||||
|
||||
private async addCloneToLibrary(
|
||||
embeddable: IEmbeddable,
|
||||
objectIdToClone: string
|
||||
): Promise<string> {
|
||||
const savedObjectToClone = await this.core.savedObjects.client.get<SavedObject>(
|
||||
embeddable.type,
|
||||
objectIdToClone
|
||||
);
|
||||
|
||||
// Clone the saved object
|
||||
const newTitle = await this.getCloneTitle(embeddable, savedObjectToClone.attributes.title);
|
||||
const clonedSavedObject = await this.core.savedObjects.client.create(
|
||||
embeddable.type,
|
||||
{
|
||||
..._.cloneDeep(savedObjectToClone.attributes),
|
||||
title: newTitle,
|
||||
},
|
||||
{ references: _.cloneDeep(savedObjectToClone.references) }
|
||||
);
|
||||
return clonedSavedObject.id;
|
||||
}
|
||||
|
||||
private async cloneEmbeddable(
|
||||
panelToClone: DashboardPanelState,
|
||||
embeddableType: string
|
||||
embeddable: IEmbeddable
|
||||
): Promise<Partial<PanelState>> {
|
||||
const panelState: PanelState<EmbeddableInput> = {
|
||||
type: embeddableType,
|
||||
explicitInput: {
|
||||
...panelToClone.explicitInput,
|
||||
id: uuid.v4(),
|
||||
},
|
||||
};
|
||||
let newTitle: string = '';
|
||||
if (panelToClone.explicitInput.savedObjectId) {
|
||||
// Fetch existing saved object
|
||||
const savedObjectToClone = await this.core.savedObjects.client.get<SavedObject>(
|
||||
embeddableType,
|
||||
panelToClone.explicitInput.savedObjectId
|
||||
);
|
||||
|
||||
// Clone the saved object
|
||||
newTitle = await this.getUniqueTitle(savedObjectToClone.attributes.title, embeddableType);
|
||||
const clonedSavedObject = await this.core.savedObjects.client.create(
|
||||
embeddableType,
|
||||
{
|
||||
..._.cloneDeep(savedObjectToClone.attributes),
|
||||
let panelState: PanelState<EmbeddableInput>;
|
||||
if (isReferenceOrValueEmbeddable(embeddable)) {
|
||||
const newTitle = await this.getCloneTitle(embeddable, embeddable.getTitle() || '');
|
||||
panelState = {
|
||||
type: embeddable.type,
|
||||
explicitInput: {
|
||||
...(await embeddable.getInputAsValueType()),
|
||||
id: uuid.v4(),
|
||||
title: newTitle,
|
||||
hidePanelTitles: panelToClone.explicitInput.hidePanelTitles,
|
||||
},
|
||||
{ references: _.cloneDeep(savedObjectToClone.references) }
|
||||
);
|
||||
(panelState.explicitInput as SavedObjectEmbeddableInput).savedObjectId = clonedSavedObject.id;
|
||||
};
|
||||
} else {
|
||||
panelState = {
|
||||
type: embeddable.type,
|
||||
explicitInput: {
|
||||
...panelToClone.explicitInput,
|
||||
id: uuid.v4(),
|
||||
},
|
||||
};
|
||||
if (panelToClone.explicitInput.savedObjectId) {
|
||||
const clonedSavedObjectId = await this.addCloneToLibrary(
|
||||
embeddable,
|
||||
panelToClone.explicitInput.savedObjectId
|
||||
);
|
||||
(panelState.explicitInput as SavedObjectEmbeddableInput).savedObjectId =
|
||||
clonedSavedObjectId;
|
||||
}
|
||||
}
|
||||
this.core.notifications.toasts.addSuccess({
|
||||
title: dashboardClonePanelAction.getSuccessMessage(),
|
||||
|
|
|
@ -79,9 +79,6 @@ export class UnlinkFromLibraryAction implements Action<UnlinkFromLibraryActionCo
|
|||
type: embeddable.type,
|
||||
explicitInput: { ...newInput, title: embeddable.getTitle() },
|
||||
};
|
||||
// since by value visualizations should not have default titles, unlinking a visualization should remove
|
||||
// the library title from the attributes.
|
||||
_.unset(newPanel, 'explicitInput.attributes.title');
|
||||
dashboard.replacePanel(panelToReplace, newPanel, true);
|
||||
|
||||
const title = dashboardUnlinkFromLibraryAction.getSuccessMessage(
|
||||
|
|
|
@ -105,6 +105,20 @@ export class DashboardContainer extends Container<InheritedChildInput, Dashboard
|
|||
return Object.keys(this.getInput().panels).length;
|
||||
};
|
||||
|
||||
public async getPanelTitles(): Promise<string[]> {
|
||||
const titles: string[] = [];
|
||||
const ids: string[] = Object.keys(this.getInput().panels);
|
||||
for (const panelId of ids) {
|
||||
await this.untilEmbeddableLoaded(panelId);
|
||||
const child: IEmbeddable<EmbeddableInput, EmbeddableOutput> = this.getChild(panelId);
|
||||
const title = child.getTitle();
|
||||
if (title) {
|
||||
titles.push(title);
|
||||
}
|
||||
}
|
||||
return titles;
|
||||
}
|
||||
|
||||
constructor(
|
||||
initialInput: DashboardContainerInput,
|
||||
private readonly services: DashboardContainerServices,
|
||||
|
|
|
@ -137,10 +137,14 @@ export class AttributeService<
|
|||
return input;
|
||||
}
|
||||
const { attributes } = await this.unwrapAttributes(input);
|
||||
const libraryTitle = attributes.title;
|
||||
const { savedObjectId, ...originalInputToPropagate } = input;
|
||||
|
||||
return {
|
||||
...originalInputToPropagate,
|
||||
attributes,
|
||||
// by value visualizations should not have default titles and/or descriptions
|
||||
...{ attributes: omit(attributes, ['title', 'description']) },
|
||||
title: libraryTitle,
|
||||
} as unknown as ValType;
|
||||
};
|
||||
|
||||
|
|
|
@ -450,10 +450,8 @@ export class VisualizeEmbeddable
|
|||
const input = {
|
||||
savedVis: this.vis.serialize(),
|
||||
};
|
||||
if (this.getTitle()) {
|
||||
input.savedVis.title = this.getTitle();
|
||||
}
|
||||
delete input.savedVis.id;
|
||||
_.unset(input, 'savedVis.title');
|
||||
return new Promise<VisualizeByValueInput>((resolve) => {
|
||||
resolve({ ...(input as VisualizeByValueInput) });
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const dashboardPanelActions = getService('dashboardPanelActions');
|
||||
const dashboardAddPanel = getService('dashboardAddPanel');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects([
|
||||
'dashboard',
|
||||
'header',
|
||||
|
@ -53,6 +54,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(panelDimensions[0]).to.eql(panelDimensions[1]);
|
||||
});
|
||||
|
||||
it('clone of a by reference embeddable is by value', async () => {
|
||||
const panelName = PIE_CHART_VIS_NAME.replace(/\s+/g, '');
|
||||
const clonedPanel = await testSubjects.find(`embeddablePanelHeading-${panelName}(copy)`);
|
||||
const descendants = await testSubjects.findAllDescendant(
|
||||
'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION',
|
||||
clonedPanel
|
||||
);
|
||||
expect(descendants.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('gives a correct title to the clone of a clone', async () => {
|
||||
const initialPanelTitles = await PageObjects.dashboard.getPanelTitles();
|
||||
const clonedPanelName = initialPanelTitles[initialPanelTitles.length - 1];
|
||||
|
@ -65,5 +76,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
PIE_CHART_VIS_NAME + ' (copy 1)'
|
||||
);
|
||||
});
|
||||
|
||||
it('clone of a by value embeddable is by value', async () => {
|
||||
const panelName = PIE_CHART_VIS_NAME.replace(/\s+/g, '');
|
||||
const clonedPanel = await testSubjects.find(`embeddablePanelHeading-${panelName}(copy1)`);
|
||||
const descendants = await testSubjects.findAllDescendant(
|
||||
'embeddablePanelNotification-ACTION_LIBRARY_NOTIFICATION',
|
||||
clonedPanel
|
||||
);
|
||||
expect(descendants.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue