* Aligned Lens & Visualize Top nav behaviour and look
This commit is contained in:
parent
39e02b6b75
commit
28365e322e
|
@ -98,7 +98,6 @@ const TopNav = ({
|
|||
stateTransfer: services.stateTransferService,
|
||||
savedObjectsClient,
|
||||
embeddableId,
|
||||
onAppLeave,
|
||||
},
|
||||
services
|
||||
);
|
||||
|
@ -117,7 +116,6 @@ const TopNav = ({
|
|||
services,
|
||||
embeddableId,
|
||||
savedObjectsClient,
|
||||
onAppLeave,
|
||||
]);
|
||||
const [indexPatterns, setIndexPatterns] = useState<IndexPattern[]>(
|
||||
vis.data.indexPattern ? [vis.data.indexPattern] : []
|
||||
|
@ -145,8 +143,9 @@ const TopNav = ({
|
|||
// Confirm when the user has made any changes to an existing visualizations
|
||||
// or when the user has configured something without saving
|
||||
if (
|
||||
((originatingApp && originatingApp === 'dashboards') || originatingApp === 'canvas') &&
|
||||
(hasUnappliedChanges || hasUnsavedChanges)
|
||||
originatingApp &&
|
||||
(hasUnappliedChanges || hasUnsavedChanges) &&
|
||||
!services.stateTransferService.isTransferInProgress
|
||||
) {
|
||||
return actions.confirm(
|
||||
i18n.translate('visualize.confirmModal.confirmTextDescription', {
|
||||
|
@ -161,10 +160,11 @@ const TopNav = ({
|
|||
});
|
||||
}, [
|
||||
onAppLeave,
|
||||
hasUnappliedChanges,
|
||||
hasUnsavedChanges,
|
||||
visualizeCapabilities.save,
|
||||
originatingApp,
|
||||
hasUnsavedChanges,
|
||||
hasUnappliedChanges,
|
||||
visualizeCapabilities.save,
|
||||
services.stateTransferService.isTransferInProgress,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -129,6 +129,7 @@ export interface SavedVisInstance {
|
|||
|
||||
export interface ByValueVisInstance {
|
||||
vis: Vis;
|
||||
savedVis: VisSavedObject;
|
||||
savedSearch?: SavedObject;
|
||||
embeddableHandler: VisualizeEmbeddableContract;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TopNavMenuData } from 'src/plugins/navigation/public';
|
||||
import { AppMountParameters } from 'kibana/public';
|
||||
import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from '../../../../visualizations/public';
|
||||
import {
|
||||
showSaveModal,
|
||||
|
@ -55,7 +54,6 @@ interface TopNavConfigParams {
|
|||
stateTransfer: EmbeddableStateTransfer;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
embeddableId?: string;
|
||||
onAppLeave: AppMountParameters['onAppLeave'];
|
||||
}
|
||||
|
||||
export const getTopNavConfig = (
|
||||
|
@ -72,12 +70,10 @@ export const getTopNavConfig = (
|
|||
visualizationIdFromUrl,
|
||||
stateTransfer,
|
||||
embeddableId,
|
||||
onAppLeave,
|
||||
}: TopNavConfigParams,
|
||||
{
|
||||
application,
|
||||
chrome,
|
||||
embeddable,
|
||||
history,
|
||||
share,
|
||||
setActiveUrl,
|
||||
|
@ -89,14 +85,11 @@ export const getTopNavConfig = (
|
|||
}: VisualizeServices
|
||||
) => {
|
||||
const { vis, embeddableHandler } = visInstance;
|
||||
const savedVis = 'savedVis' in visInstance ? visInstance.savedVis : undefined;
|
||||
const savedVis = visInstance.savedVis;
|
||||
/**
|
||||
* Called when the user clicks "Save" button.
|
||||
*/
|
||||
async function doSave(saveOptions: SavedObjectSaveOpts) {
|
||||
if (!savedVis) {
|
||||
return {};
|
||||
}
|
||||
const newlyCreated = !Boolean(savedVis.id) || savedVis.copyOnSave;
|
||||
// vis.title was not bound and it's needed to reflect title into visState
|
||||
stateContainer.transitions.setVis({
|
||||
|
@ -122,15 +115,21 @@ export const getTopNavConfig = (
|
|||
});
|
||||
|
||||
if (originatingApp && saveOptions.returnToOrigin) {
|
||||
const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`;
|
||||
if (!embeddableId) {
|
||||
const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`;
|
||||
|
||||
// Manually insert a new url so the back button will open the saved visualization.
|
||||
history.replace(appPath);
|
||||
setActiveUrl(appPath);
|
||||
// Manually insert a new url so the back button will open the saved visualization.
|
||||
history.replace(appPath);
|
||||
setActiveUrl(appPath);
|
||||
}
|
||||
|
||||
if (newlyCreated && stateTransfer) {
|
||||
stateTransfer.navigateToWithEmbeddablePackage(originatingApp, {
|
||||
state: { type: VISUALIZE_EMBEDDABLE_TYPE, input: { savedObjectId: id } },
|
||||
state: {
|
||||
type: VISUALIZE_EMBEDDABLE_TYPE,
|
||||
input: { savedObjectId: id },
|
||||
embeddableId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
application.navigateToApp(originatingApp);
|
||||
|
@ -192,6 +191,24 @@ export const getTopNavConfig = (
|
|||
}
|
||||
};
|
||||
|
||||
const saveButtonLabel =
|
||||
embeddableId ||
|
||||
(!savedVis.id && dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && originatingApp)
|
||||
? i18n.translate('visualize.topNavMenu.saveVisualizationToLibraryButtonLabel', {
|
||||
defaultMessage: 'Save to library',
|
||||
})
|
||||
: originatingApp && (embeddableId || savedVis.id)
|
||||
? i18n.translate('visualize.topNavMenu.saveVisualizationAsButtonLabel', {
|
||||
defaultMessage: 'Save as',
|
||||
})
|
||||
: i18n.translate('visualize.topNavMenu.saveVisualizationButtonLabel', {
|
||||
defaultMessage: 'Save',
|
||||
});
|
||||
|
||||
const showSaveAndReturn =
|
||||
originatingApp &&
|
||||
(savedVis?.id || dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables);
|
||||
|
||||
const topNavMenu: TopNavMenuData[] = [
|
||||
{
|
||||
id: 'inspector',
|
||||
|
@ -243,7 +260,7 @@ export const getTopNavConfig = (
|
|||
// disable the Share button if no action specified
|
||||
disableButton: !share || !!embeddableId,
|
||||
},
|
||||
...(originatingApp === 'dashboards' || originatingApp === 'canvas'
|
||||
...(originatingApp
|
||||
? [
|
||||
{
|
||||
id: 'cancel',
|
||||
|
@ -268,24 +285,16 @@ export const getTopNavConfig = (
|
|||
},
|
||||
]
|
||||
: []),
|
||||
...(visualizeCapabilities.save && !embeddableId
|
||||
...(visualizeCapabilities.save
|
||||
? [
|
||||
{
|
||||
id: 'save',
|
||||
iconType: savedVis?.id && originatingApp ? undefined : 'save',
|
||||
label:
|
||||
savedVis?.id && originatingApp
|
||||
? i18n.translate('visualize.topNavMenu.saveVisualizationAsButtonLabel', {
|
||||
defaultMessage: 'save as',
|
||||
})
|
||||
: i18n.translate('visualize.topNavMenu.saveVisualizationButtonLabel', {
|
||||
defaultMessage: 'save',
|
||||
}),
|
||||
emphasize: (savedVis && !savedVis.id) || !originatingApp,
|
||||
iconType: showSaveAndReturn ? undefined : 'save',
|
||||
label: saveButtonLabel,
|
||||
emphasize: !showSaveAndReturn,
|
||||
description: i18n.translate('visualize.topNavMenu.saveVisualizationButtonAriaLabel', {
|
||||
defaultMessage: 'Save Visualization',
|
||||
}),
|
||||
className: savedVis?.id && originatingApp ? 'saveAsButton' : '',
|
||||
testId: 'visualizeSaveButton',
|
||||
disableButton: hasUnappliedChanges,
|
||||
tooltip() {
|
||||
|
@ -298,7 +307,7 @@ export const getTopNavConfig = (
|
|||
);
|
||||
}
|
||||
},
|
||||
run: (anchorElement: HTMLElement) => {
|
||||
run: () => {
|
||||
const onSave = async ({
|
||||
newTitle,
|
||||
newCopyOnSave,
|
||||
|
@ -308,10 +317,6 @@ export const getTopNavConfig = (
|
|||
returnToOrigin,
|
||||
dashboardId,
|
||||
}: OnSaveProps & { returnToOrigin?: boolean } & { dashboardId?: string | null }) => {
|
||||
if (!savedVis) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTitle = savedVis.title;
|
||||
savedVis.title = newTitle;
|
||||
embeddableHandler.updateInput({ title: newTitle });
|
||||
|
@ -371,12 +376,10 @@ export const getTopNavConfig = (
|
|||
let selectedTags: string[] = [];
|
||||
let tagOptions: React.ReactNode | undefined;
|
||||
|
||||
if (
|
||||
savedVis &&
|
||||
savedObjectsTagging &&
|
||||
savedObjectsTagging.ui.hasTagDecoration(savedVis)
|
||||
) {
|
||||
selectedTags = savedVis.getTags();
|
||||
if (savedObjectsTagging) {
|
||||
if (savedVis && savedObjectsTagging.ui.hasTagDecoration(savedVis)) {
|
||||
selectedTags = savedVis.getTags();
|
||||
}
|
||||
tagOptions = (
|
||||
<savedObjectsTagging.ui.components.SavedObjectSaveModalTagSelector
|
||||
initialSelection={selectedTags}
|
||||
|
@ -398,6 +401,16 @@ export const getTopNavConfig = (
|
|||
objectType={'visualization'}
|
||||
onClose={() => {}}
|
||||
originatingApp={originatingApp}
|
||||
returnToOriginSwitchLabel={
|
||||
originatingApp && embeddableId
|
||||
? i18n.translate('visualize.topNavMenu.updatePanel', {
|
||||
defaultMessage: 'Update panel on {originatingAppName}',
|
||||
values: {
|
||||
originatingAppName: stateTransfer.getAppNameFromId(originatingApp),
|
||||
},
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<SavedObjectSaveModalDashboard
|
||||
|
@ -409,25 +422,12 @@ export const getTopNavConfig = (
|
|||
savedObjectsClient={savedObjectsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
const isSaveAsButton = anchorElement.classList.contains('saveAsButton');
|
||||
onAppLeave((actions) => {
|
||||
return actions.default();
|
||||
});
|
||||
if (
|
||||
originatingApp === 'dashboards' &&
|
||||
dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables &&
|
||||
!isSaveAsButton
|
||||
) {
|
||||
createVisReference();
|
||||
} else if (savedVis) {
|
||||
showSaveModal(saveModal, I18nContext);
|
||||
}
|
||||
showSaveModal(saveModal, I18nContext);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(originatingApp && ((savedVis && savedVis.id) || embeddableId)
|
||||
...(visualizeCapabilities.save && showSaveAndReturn
|
||||
? [
|
||||
{
|
||||
id: 'saveAndReturn',
|
||||
|
@ -455,20 +455,13 @@ export const getTopNavConfig = (
|
|||
}
|
||||
},
|
||||
run: async () => {
|
||||
if (!savedVis?.id) {
|
||||
return createVisReference();
|
||||
}
|
||||
const saveOptions = {
|
||||
confirmOverwrite: false,
|
||||
returnToOrigin: true,
|
||||
};
|
||||
onAppLeave((actions) => {
|
||||
return actions.default();
|
||||
});
|
||||
if (
|
||||
originatingApp === 'dashboards' &&
|
||||
dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables &&
|
||||
!savedVis
|
||||
) {
|
||||
return createVisReference();
|
||||
}
|
||||
return doSave(saveOptions);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -71,8 +71,14 @@ export const getVisualizationInstanceFromInput = async (
|
|||
visualizeServices: VisualizeServices,
|
||||
input: VisualizeInput
|
||||
) => {
|
||||
const { visualizations } = visualizeServices;
|
||||
const { visualizations, savedVisualizations } = visualizeServices;
|
||||
const visState = input.savedVis as SerializedVis;
|
||||
|
||||
/**
|
||||
* A saved vis is needed even in by value mode to support 'save to library' which converts the 'by value'
|
||||
* state of the visualization, into a new saved object.
|
||||
*/
|
||||
const savedVis: VisSavedObject = await savedVisualizations.get();
|
||||
let vis = await visualizations.createVis(visState.type, cloneDeep(visState));
|
||||
if (vis.type.setup) {
|
||||
try {
|
||||
|
@ -87,6 +93,7 @@ export const getVisualizationInstanceFromInput = async (
|
|||
);
|
||||
return {
|
||||
vis,
|
||||
savedVis,
|
||||
embeddableHandler,
|
||||
savedSearch,
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ export const useSavedVisInstance = (
|
|||
savedVisInstance?: SavedVisInstance;
|
||||
visEditorController?: IEditorController;
|
||||
}>({});
|
||||
|
||||
const visEditorRef = useRef<HTMLDivElement | null>(null);
|
||||
const visId = useRef('');
|
||||
|
||||
|
@ -132,7 +133,6 @@ export const useSavedVisInstance = (
|
|||
embeddableHandler.render(visEditorRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
setState({
|
||||
savedVisInstance,
|
||||
visEditorController,
|
||||
|
@ -189,13 +189,13 @@ export const useSavedVisInstance = (
|
|||
getSavedVisInstance();
|
||||
}
|
||||
}, [
|
||||
eventEmitter,
|
||||
isChromeVisible,
|
||||
originatingApp,
|
||||
services,
|
||||
eventEmitter,
|
||||
originatingApp,
|
||||
isChromeVisible,
|
||||
visualizationIdFromUrl,
|
||||
state.savedVisInstance,
|
||||
state.visEditorController,
|
||||
visualizationIdFromUrl,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -149,8 +149,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
|
|||
await PageObjects.visualize.clickAggBasedVisualizations();
|
||||
await PageObjects.visualize.clickMetric();
|
||||
await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)');
|
||||
await testSubjects.exists('visualizeSaveButton');
|
||||
await testSubjects.click('visualizeSaveButton');
|
||||
await testSubjects.exists('visualizesaveAndReturnButton');
|
||||
await testSubjects.click('visualizesaveAndReturnButton');
|
||||
}
|
||||
|
||||
async createAndEmbedMarkdown({ name, markdown }: { name: string; markdown: string }) {
|
||||
|
@ -163,7 +163,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
|
|||
await PageObjects.visualize.clickMarkdownWidget();
|
||||
await PageObjects.visEditor.setMarkdownTxt(markdown);
|
||||
await PageObjects.visEditor.clickGo();
|
||||
await testSubjects.click('visualizeSaveButton');
|
||||
await testSubjects.click('visualizesaveAndReturnButton');
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ export function getLensTopNavConfig(options: {
|
|||
if (showCancel) {
|
||||
topNavMenu.push({
|
||||
label: i18n.translate('xpack.lens.app.cancel', {
|
||||
defaultMessage: 'cancel',
|
||||
defaultMessage: 'Cancel',
|
||||
}),
|
||||
run: actions.cancel,
|
||||
testId: 'lnsApp_cancelButton',
|
||||
|
|
Loading…
Reference in a new issue