From b0252adf8de3d3dabe30781935b1e1eebc904688 Mon Sep 17 00:00:00 2001 From: Suchit Sahoo Date: Mon, 19 Aug 2024 23:19:15 +0000 Subject: [PATCH] Add new Page Header in Vis Builder Signed-off-by: Suchit Sahoo --- .../dashboard_listing/dashboard_listing.tsx | 63 ++++--- .../table_list_view/table_list_view.tsx | 21 ++- .../vis_builder/public/application/app.tsx | 13 ++ .../public/application/components/top_nav.tsx | 47 ++++- .../application/utils/get_top_nav_config.tsx | 176 +++++++++++++----- .../visualize/public/application/app.tsx | 12 -- .../components/visualize_byvalue_editor.tsx | 12 ++ .../components/visualize_editor.tsx | 12 ++ .../components/visualize_listing.tsx | 97 +++++++--- .../components/visualize_top_nav.tsx | 2 +- .../application/utils/get_top_nav_config.tsx | 2 +- 11 files changed, 326 insertions(+), 131 deletions(-) diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/components/dashboard_listing/dashboard_listing.tsx index 00ce5a713b7f..e145f397d4c0 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/dashboard_listing.tsx @@ -34,6 +34,7 @@ export const DashboardListing = () => { dashboardProviders, data: { query }, osdUrlStateStorage, + navigation, }, } = useOpenSearchDashboards(); @@ -41,6 +42,9 @@ export const DashboardListing = () => { const queryParameters = useMemo(() => new URLSearchParams(location.search), [location]); const initialFiltersFromURL = queryParameters.get('filter'); const [initialFilter, setInitialFilter] = useState(initialFiltersFromURL); + const showUpdatedUx = uiSettings?.get('home:useNewHomePage'); + const { HeaderControl } = navigation.ui; + const { setAppRightControls } = application; useEffect(() => { // syncs `_g` portion of url with query services @@ -201,31 +205,40 @@ export const DashboardListing = () => { ); }); + const createButton = ; + return ( - - } - findItems={find} - deleteItems={hideWriteControls ? undefined : deleteItems} - editItem={hideWriteControls ? undefined : editItem} - tableColumns={tableColumns} - listingLimit={listingLimit} - initialFilter={initialFilter ?? ''} - initialPageSize={initialPageSize} - noItemsFragment={noItemsFragment} - entityName={i18n.translate('dashboard.listing.table.entityName', { - defaultMessage: 'dashboard', - })} - entityNamePlural={i18n.translate('dashboard.listing.table.entityNamePlural', { - defaultMessage: 'dashboards', - })} - tableListTitle={i18n.translate('dashboard.listing.dashboardsTitle', { - defaultMessage: 'Dashboards', - })} - toastNotifications={notifications.toasts} - /> + <> + {showUpdatedUx && !hideWriteControls && ( + + )} + + ); }; diff --git a/src/plugins/opensearch_dashboards_react/public/table_list_view/table_list_view.tsx b/src/plugins/opensearch_dashboards_react/public/table_list_view/table_list_view.tsx index 3994bd3e4f7b..8da90e3428e5 100644 --- a/src/plugins/opensearch_dashboards_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/opensearch_dashboards_react/public/table_list_view/table_list_view.tsx @@ -84,6 +84,7 @@ export interface TableListViewProps { headingId?: string; restrictWidth?: boolean; paddingSize?: EuiPageProps['paddingSize']; + showUpdatedUx?: boolean; } export interface TableListViewState { @@ -525,15 +526,17 @@ class TableListView extends React.Component {this.state.showDeleteModal && this.renderConfirmDeleteModal()} - - - -

{this.props.tableListTitle}

-
-
- - {this.props.createButton || defaultCreateButton} -
+ {!this.props.showUpdatedUx && ( + + + +

{this.props.tableListTitle}

+
+
+ + {this.props.createButton || defaultCreateButton} +
+ )} diff --git a/src/plugins/vis_builder/public/application/app.tsx b/src/plugins/vis_builder/public/application/app.tsx index 9a3367651fc2..515567ddcc4c 100644 --- a/src/plugins/vis_builder/public/application/app.tsx +++ b/src/plugins/vis_builder/public/application/app.tsx @@ -15,6 +15,7 @@ import { RightNav } from './components/right_nav'; import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public'; import { VisBuilderServices } from '../types'; import { syncQueryStateWithUrl } from '../../../data/public'; +import { HeaderVariant } from '../../../../core/public/index'; import './app.scss'; @@ -23,9 +24,21 @@ export const VisBuilderApp = () => { services: { data: { query }, osdUrlStateStorage, + chrome, + uiSettings, }, } = useOpenSearchDashboards(); const { pathname } = useLocation(); + const { setHeaderVariant } = chrome; + const showActionsInGroup = uiSettings.get('home:useNewHomePage'); + + useEffect(() => { + if (showActionsInGroup) setHeaderVariant?.(HeaderVariant.APPLICATION); + + return () => { + setHeaderVariant?.(); + }; + }, [setHeaderVariant, showActionsInGroup]); useEffect(() => { // syncs `_g` portion of url with query services diff --git a/src/plugins/vis_builder/public/application/components/top_nav.tsx b/src/plugins/vis_builder/public/application/components/top_nav.tsx index 8361073bcd14..3e8ff11254f7 100644 --- a/src/plugins/vis_builder/public/application/components/top_nav.tsx +++ b/src/plugins/vis_builder/public/application/components/top_nav.tsx @@ -8,7 +8,7 @@ import { isEqual } from 'lodash'; import { useParams } from 'react-router-dom'; import { useUnmount } from 'react-use'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; -import { getTopNavConfig } from '../utils/get_top_nav_config'; +import { getLegacyTopNavConfig, getNavActions, getTopNavConfig } from '../utils/get_top_nav_config'; import { VisBuilderServices } from '../../types'; import './top_nav.scss'; @@ -18,7 +18,7 @@ import { setSavedQuery } from '../utils/state_management/visualization_slice'; import { setEditorState } from '../utils/state_management/metadata_slice'; import { useCanSave } from '../utils/use/use_can_save'; import { saveStateToSavedObject } from '../../saved_visualizations/transforms'; -import { TopNavMenuData } from '../../../../navigation/public'; +import { TopNavMenuData, TopNavMenuItemRenderType } from '../../../../navigation/public'; import { opensearchFilters, connectStorageToQueryState } from '../../../../data/public'; import { RootState } from '../../../../data_explorer/public'; @@ -41,11 +41,13 @@ export const TopNav = () => { navigation: { ui: { TopNavMenu }, }, + uiSettings, appName, capabilities, } = services; const rootState = useTypedSelector((state: RootState) => state); const dispatch = useTypedDispatch(); + const showActionsInGroup = uiSettings.get('home:useNewHomePage'); useDeepEffect(() => { dispatch(setEditorState({ state: 'dirty' })); @@ -67,7 +69,7 @@ export const TopNav = () => { const getConfig = () => { if (!savedVisBuilderVis || !indexPattern) return; - return getTopNavConfig( + const navActions = getNavActions( { visualizationIdFromUrl, savedVisBuilderVis: saveStateToSavedObject(savedVisBuilderVis, rootState, indexPattern), @@ -77,6 +79,38 @@ export const TopNav = () => { }, services ); + + return showActionsInGroup + ? getTopNavConfig( + { + visualizationIdFromUrl, + savedVisBuilderVis: saveStateToSavedObject( + savedVisBuilderVis, + rootState, + indexPattern + ), + saveDisabledReason, + dispatch, + originatingApp, + }, + services, + navActions + ) + : getLegacyTopNavConfig( + { + visualizationIdFromUrl, + savedVisBuilderVis: saveStateToSavedObject( + savedVisBuilderVis, + rootState, + indexPattern + ), + saveDisabledReason, + dispatch, + originatingApp, + }, + services, + navActions + ); }; setConfig(getConfig()); @@ -89,6 +123,7 @@ export const TopNav = () => { dispatch, indexPattern, originatingApp, + showActionsInGroup, ]); // reset validity before component destroyed @@ -109,11 +144,15 @@ export const TopNav = () => { setMenuMountPoint={setHeaderActionMenu} indexPatterns={indexPattern ? [indexPattern] : []} showDatePicker={!!indexPattern?.timeFieldName ?? true} - showSearchBar + showSearchBar={TopNavMenuItemRenderType.IN_PORTAL} showSaveQuery={showSaveQuery} useDefaultBehaviors savedQueryId={rootState.visualization.savedQuery} onSavedQueryIdChange={updateSavedQueryId} + groupActions={showActionsInGroup} + screenTitle={ + savedVisBuilderVis?.title.length ? savedVisBuilderVis?.title : 'New visualization' + } /> ); diff --git a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx index 945e09a09734..42f3e68c3898 100644 --- a/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/vis_builder/public/application/utils/get_top_nav_config.tsx @@ -48,8 +48,11 @@ export interface TopNavConfigParams { dispatch: AppDispatch; originatingApp?: string; } +interface VisBuilderNavActionMap { + [key: string]: (anchorElement?: any) => void; +} -export const getTopNavConfig = ( +export const getLegacyTopNavConfig = ( { visualizationIdFromUrl, savedVisBuilderVis, @@ -57,15 +60,9 @@ export const getTopNavConfig = ( dispatch, originatingApp, }: TopNavConfigParams, - services: VisBuilderServices + services: VisBuilderServices, + navActions: VisBuilderNavActionMap ) => { - const { - i18n: { Context: I18nContext }, - embeddable, - } = services; - - const stateTransfer = embeddable.getStateTransfer(); - const topNavConfig: TopNavMenuData[] = [ { id: 'save', @@ -86,26 +83,7 @@ export const getTopNavConfig = ( testId: 'visBuilderSaveButton', disableButton: !!saveDisabledReason, tooltip: saveDisabledReason, - run: (_anchorElement) => { - const saveModal = ( - {}} - originatingApp={originatingApp} - getAppNameFromId={stateTransfer.getAppNameFromId} - /> - ); - - showSaveModal(saveModal, I18nContext); - }, + run: navActions.save, }, ...(originatingApp && savedVisBuilderVis && savedVisBuilderVis.id ? [ @@ -125,25 +103,7 @@ export const getTopNavConfig = ( testId: 'visBuilderSaveAndReturnButton', disableButton: !!saveDisabledReason, tooltip: saveDisabledReason, - run: async () => { - const saveOptions = { - newTitle: savedVisBuilderVis.title, - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - newDescription: savedVisBuilderVis.description, - returnToOrigin: true, - }; - - const onSave = getOnSave( - savedVisBuilderVis, - originatingApp, - visualizationIdFromUrl, - dispatch, - services - ); - - return onSave(saveOptions); - }, + run: navActions.saveAndReturn, }, ] : []), @@ -152,6 +112,61 @@ export const getTopNavConfig = ( return topNavConfig; }; +export const getTopNavConfig = ( + { + visualizationIdFromUrl, + savedVisBuilderVis, + saveDisabledReason, + dispatch, + originatingApp, + }: TopNavConfigParams, + services: VisBuilderServices, + navActions: VisBuilderNavActionMap +) => { + const topNavMenu = [ + { + tooltip: !!saveDisabledReason + ? saveDisabledReason + : savedVisBuilderVis?.id && originatingApp + ? i18n.translate('visBuilder.topNavMenu.saveVisualizationAsButtonLabel', { + defaultMessage: 'save as', + }) + : i18n.translate('visBuilder.topNavMenu.saveVisualizationButtonLabel', { + defaultMessage: 'save', + }), + ariaLabel: i18n.translate('visBuilder.topNavMenu.saveVisualizationAsButtonLabel', { + defaultMessage: 'save', + }), + testId: 'visBuilderSaveButton', + run: navActions.save, + iconType: 'save', + controlType: 'icon', + disabled: !!saveDisabledReason, + }, + ...(originatingApp && savedVisBuilderVis && savedVisBuilderVis.id + ? [ + { + tooltip: i18n.translate('visualize.topNavMenu.openInspectorTooltip', { + defaultMessage: 'Save and return', + }), + ariaLabel: i18n.translate( + 'visBuilder.topNavMenu.saveAndReturnVisualizationButtonAriaLabel', + { + defaultMessage: 'Finish editing visBuilder and return to the last app', + } + ), + testId: 'visBuilderSaveAndReturnButton', + run: navActions.saveAndReturn, + iconType: 'checkInCircleFilled', + controlType: 'icon', + disabled: !!saveDisabledReason, + }, + ] + : []), + ]; + return topNavMenu as TopNavMenuData[]; +}; + export const getOnSave = ( savedVisBuilderVis, originatingApp, @@ -258,3 +273,68 @@ export const getOnSave = ( }; return onSave; }; + +export const getNavActions = ( + { + visualizationIdFromUrl, + savedVisBuilderVis, + saveDisabledReason, + dispatch, + originatingApp, + }: TopNavConfigParams, + services: VisBuilderServices +): VisBuilderNavActionMap => { + const { + i18n: { Context: I18nContext }, + embeddable, + } = services; + + const stateTransfer = embeddable.getStateTransfer(); + + const navActions: any = {}; + + const saveAndReturnNavAction = async () => { + const saveOptions = { + newTitle: savedVisBuilderVis.title, + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + newDescription: savedVisBuilderVis.description, + returnToOrigin: true, + }; + + const onSave = getOnSave( + savedVisBuilderVis, + originatingApp, + visualizationIdFromUrl, + dispatch, + services + ); + + return onSave(saveOptions); + }; + navActions.saveAndReturn = saveAndReturnNavAction; + + const saveNavAction = (_anchorElement) => { + const saveModal = ( + {}} + originatingApp={originatingApp} + getAppNameFromId={stateTransfer.getAppNameFromId} + /> + ); + + showSaveModal(saveModal, I18nContext); + }; + + navActions.save = saveNavAction; + return navActions; +}; diff --git a/src/plugins/visualize/public/application/app.tsx b/src/plugins/visualize/public/application/app.tsx index 2057f7ab9409..476d8aba05d2 100644 --- a/src/plugins/visualize/public/application/app.tsx +++ b/src/plugins/visualize/public/application/app.tsx @@ -54,21 +54,9 @@ export const VisualizeApp = ({ onAppLeave }: VisualizeAppProps) => { services: { data: { query }, osdUrlStateStorage, - chrome, - uiSettings, }, } = useOpenSearchDashboards(); const { pathname } = useLocation(); - const { setHeaderVariant } = chrome; - const showActionsInGroup = uiSettings.get('home:useNewHomePage'); - - useEffect(() => { - if (showActionsInGroup) setHeaderVariant?.(HeaderVariant.APPLICATION); - - return () => { - setHeaderVariant?.(); - }; - }, [setHeaderVariant, showActionsInGroup]); useEffect(() => { // syncs `_g` portion of url with query services diff --git a/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx b/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx index 93a15b043c77..af52b010ae8f 100644 --- a/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx +++ b/src/plugins/visualize/public/application/components/visualize_byvalue_editor.tsx @@ -44,6 +44,7 @@ import { import { VisualizeServices } from '../types'; import { VisualizeEditorCommon } from './visualize_editor_common'; import { VisualizeAppProps } from '../app'; +import { HeaderVariant } from '../../../../../core/public/index'; export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => { const [originatingApp, setOriginatingApp] = useState(); @@ -52,6 +53,17 @@ export const VisualizeByValueEditor = ({ onAppLeave }: VisualizeAppProps) => { const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [embeddableId, setEmbeddableId] = useState(); const [valueInput, setValueInput] = useState(); + const { chrome, uiSettings } = services; + const showActionsInGroup = uiSettings.get('home:useNewHomePage'); + const { setHeaderVariant } = chrome; + + useEffect(() => { + if (showActionsInGroup) setHeaderVariant?.(HeaderVariant.APPLICATION); + + return () => { + setHeaderVariant?.(); + }; + }, [setHeaderVariant, showActionsInGroup]); useEffect(() => { const { originatingApp: value, embeddableId: embeddableIdValue, valueInput: valueInputValue } = diff --git a/src/plugins/visualize/public/application/components/visualize_editor.tsx b/src/plugins/visualize/public/application/components/visualize_editor.tsx index c7e2378a35d6..df7491fb6487 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor.tsx +++ b/src/plugins/visualize/public/application/components/visualize_editor.tsx @@ -44,6 +44,7 @@ import { import { VisualizeServices } from '../types'; import { VisualizeEditorCommon } from './visualize_editor_common'; import { VisualizeAppProps } from '../app'; +import { HeaderVariant } from '../../../../../core/public/index'; export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => { const { id: visualizationIdFromUrl } = useParams<{ id: string }>(); @@ -51,6 +52,9 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => { const { services } = useOpenSearchDashboards(); const [eventEmitter] = useState(new EventEmitter()); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(!visualizationIdFromUrl); + const { chrome, uiSettings } = services; + const showActionsInGroup = uiSettings.get('home:useNewHomePage'); + const { setHeaderVariant } = chrome; const isChromeVisible = useChromeVisibility(services.chrome); const { savedVisInstance, visEditorRef, visEditorController } = useSavedVisInstance( @@ -74,6 +78,14 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => { ); useLinkedSearchUpdates(services, eventEmitter, appState, savedVisInstance); + useEffect(() => { + if (showActionsInGroup) setHeaderVariant?.(HeaderVariant.APPLICATION); + + return () => { + setHeaderVariant?.(); + }; + }, [setHeaderVariant, showActionsInGroup]); + useEffect(() => { const { originatingApp: value } = services.embeddable diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index 5dab2f11051c..ead43a7b08ca 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -44,6 +44,7 @@ import { VisualizeConstants } from '../visualize_constants'; import { getTableColumns, getNoItemsMessage } from '../utils'; import { getUiActions } from '../../services'; import { SAVED_OBJECT_DELETE_TRIGGER } from '../../../../saved_objects_management/public'; +import { HeaderVariant } from '../../../../../core/public/index'; export const VisualizeListing = () => { const { @@ -58,11 +59,15 @@ export const VisualizeListing = () => { savedObjectsPublic, uiSettings, visualizeCapabilities, + navigation, }, } = useOpenSearchDashboards(); const { pathname } = useLocation(); const closeNewVisModal = useRef(() => {}); const listingLimit = savedObjectsPublic.settings.getListingLimit(); + const showUpdatedUx = uiSettings?.get('home:useNewHomePage'); + const { HeaderControl } = navigation.ui; + const { setAppRightControls } = application; useEffect(() => { if (pathname === '/new') { @@ -80,13 +85,24 @@ export const VisualizeListing = () => { }, [history, pathname, visualizations]); useMount(() => { - chrome.setBreadcrumbs([ - { - text: i18n.translate('visualize.visualizeListingBreadcrumbsTitle', { - defaultMessage: 'Visualize', - }), - }, - ]); + if (showUpdatedUx) { + chrome.setBreadcrumbs([ + { + text: i18n.translate('visualize.visualizeListingBreadcrumbsTitle', { + defaultMessage: 'Visualizations', + }), + }, + ]); + } else { + chrome.setBreadcrumbs([ + { + text: i18n.translate('visualize.visualizeListingBreadcrumbsTitle', { + defaultMessage: 'Visualize', + }), + }, + ]); + } + chrome.docTitle.change( i18n.translate('visualize.listingPageTitle', { defaultMessage: 'Visualize' }) ); @@ -163,29 +179,48 @@ export const VisualizeListing = () => { ); return ( - + <> + {showUpdatedUx && ( + + )} + + ); }; diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx index 5085b15ddf8c..1c3ba3bbb702 100644 --- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx +++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx @@ -259,7 +259,7 @@ const TopNav = ({ savedQueryId={currentAppState.savedQuery} onSavedQueryIdChange={stateContainer.transitions.updateSavedQuery} indexPatterns={indexPatterns} - screenTitle={vis.title.length ?? '' ? vis.title : 'New Visualization'} + screenTitle={vis.title.length ?? '' ? vis.title : 'New visualization'} showAutoRefreshOnly={!showDatePicker()} showDatePicker={showDatePicker()} showFilterBar={showFilterBar} diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index f5a8baccb3b4..cffd251b3eb4 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -517,7 +517,7 @@ export const getTopNavConfig = ( defaultMessage: 'Inspect', }), ariaLabel: i18n.translate('visualize.topNavMenu.openInspectorButtonLabel', { - defaultMessage: 'inspect', + defaultMessage: 'Inspect', }), testId: 'openInspectorButton', run: navActions[VisualizeTopNavIds.INSPECT],