[Time to Visualize] Align Lens & Visualize Top nav Buttons & Behaviour (#86922) (#87677)

* Aligned Lens & Visualize Top nav behaviour and look
This commit is contained in:
Devon Thomson 2021-01-07 14:55:03 -05:00 committed by GitHub
parent 39e02b6b75
commit 28365e322e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 79 deletions

View file

@ -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(() => {

View file

@ -129,6 +129,7 @@ export interface SavedVisInstance {
export interface ByValueVisInstance {
vis: Vis;
savedVis: VisSavedObject;
savedSearch?: SavedObject;
embeddableHandler: VisualizeEmbeddableContract;
}

View file

@ -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);
},
},

View file

@ -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,
};

View file

@ -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(() => {

View file

@ -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');
}
})();
}

View file

@ -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',