diff --git a/src/core/public/chrome/ui/header/_index.scss b/src/core/public/chrome/ui/header/_index.scss index f19728a52dd70..693b8bd87434f 100644 --- a/src/core/public/chrome/ui/header/_index.scss +++ b/src/core/public/chrome/ui/header/_index.scss @@ -5,7 +5,7 @@ width: 100%; position: fixed; top: 0; - z-index: 10; + z-index: $euiZHeader; } .chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) { diff --git a/src/core/public/chrome/ui/header/header_wrapper.tsx b/src/core/public/chrome/ui/header/header_wrapper.tsx index 5306faa209586..917fbfc909c50 100644 --- a/src/core/public/chrome/ui/header/header_wrapper.tsx +++ b/src/core/public/chrome/ui/header/header_wrapper.tsx @@ -21,13 +21,13 @@ import React, { FunctionComponent, useState } from 'react'; import classnames from 'classnames'; import { Header, HeaderProps } from './'; -const IS_LOCKED_KEY = 'core.chrome.isLocked'; +export const IS_NAV_LOCKED_KEY = 'core.chrome.isLocked'; export const HeaderWrapper: FunctionComponent = props => { - const initialIsLocked = localStorage.getItem(IS_LOCKED_KEY); + const initialIsLocked = localStorage.getItem(IS_NAV_LOCKED_KEY); const [isLocked, setIsLocked] = useState(initialIsLocked === 'true'); const setIsLockedStored = (locked: boolean) => { - localStorage.setItem(IS_LOCKED_KEY, `${locked}`); + localStorage.setItem(IS_NAV_LOCKED_KEY, `${locked}`); setIsLocked(locked); }; const className = classnames( diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/_app.scss b/x-pack/legacy/plugins/lens/public/app_plugin/_app.scss index 382a3f5522daf..73822988ae6b0 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/_app.scss +++ b/x-pack/legacy/plugins/lens/public/app_plugin/_app.scss @@ -1,3 +1,6 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/nav_drawer/variables'; + .lnsApp { position: absolute; top: 0; @@ -10,6 +13,10 @@ overflow: hidden; } +.lnsApp-bottomBarShowing { + padding-bottom: $euiSizeL * 2; +} + .lnsApp__header { padding: $euiSize; border-bottom: $euiBorderThin; @@ -21,3 +28,12 @@ flex-direction: column; flex-grow: 1; } + +.lnsApp__bottomBar { + left: $euiNavDrawerWidthCollapsed; + z-index: $euiZHeader + 10; +} + +.lnsApp__bottomBar-navIsLockedOpen { + left: $euiNavDrawerWidthExpanded; +} diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index 3e157fc394d30..bb9676388015c 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -6,9 +6,20 @@ import _ from 'lodash'; import React, { useState, useEffect, useCallback, useRef } from 'react'; +import classNames from 'classnames'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiLink, + EuiFlexGroup, + EuiFlexItem, + EuiBottomBar, + EuiButton, + EuiButtonEmpty, + EuiPopover, + EuiFormRow, + EuiFieldText, +} from '@elastic/eui'; import { Storage } from 'ui/storage'; import { CoreStart } from 'src/core/public'; import { Query } from '../../../../../../src/legacy/core_plugins/data/public'; @@ -64,10 +75,13 @@ export function App({ docStorage: SavedObjectStore; redirectTo: (id?: string) => void; }) { + const chromeNavIsLockedOpen = localStorage.getItem('core.chrome.isLocked'); const timeDefaults = core.uiSettings.get('timepicker:timeDefaults'); const language = store.get('kibana.userQueryLanguage') || core.uiSettings.get('search:queryLanguage'); + const [isTitlePopoverOpen, setIsTitlePopoverOpen] = useState(false); + const [state, setState] = useState({ isLoading: !!docId, isDirty: false, @@ -152,6 +166,14 @@ export function App({ [] ); + const classes = classNames('lnsApp', { + 'lnsApp-bottomBarShowing': isSaveable, + }); + + const bottomBarClasses = classNames('lnsApp__bottomBar', { + 'lnsApp__bottomBar-navIsLockedOpen': chromeNavIsLockedOpen === 'true', + }); + return ( -
+
- )}
+ + {isSaveable && ( + + + + setIsTitlePopoverOpen(!isTitlePopoverOpen)} + > + Untitled + + } + isOpen={isTitlePopoverOpen} + closePopover={() => setIsTitlePopoverOpen(false)} + panelPaddingSize="s" + ownFocus + > +
+ + + +
+
+
+ + { + if (isSaveable && lastKnownDocRef.current) { + docStorage + .save(lastKnownDocRef.current) + .then(({ id }) => { + // Prevents unnecessary network request and disables save button + const newDoc = { ...lastKnownDocRef.current!, id }; + setState({ + ...state, + isDirty: false, + persistedDoc: newDoc, + }); + if (docId !== id) { + redirectTo(id); + } + }) + .catch(reason => { + core.notifications.toasts.addDanger( + i18n.translate('xpack.lens.editorFrame.docSavingError', { + defaultMessage: 'Error saving document {reason}', + values: { reason }, + }) + ); + }); + } + }} + > + {i18n.translate('xpack.lens.editorFrame.save', { + defaultMessage: 'Save changes', + })} + + +
+
+ )} ); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx index 0b4d7ba217532..d28e833918220 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx @@ -1502,7 +1502,7 @@ describe('editor_frame', () => { query: { query: '', language: 'lucene' }, filters: [], }, - title: 'New visualization', + title: 'Untitled', type: 'lens', visualizationType: 'testVis', }, @@ -1521,7 +1521,7 @@ describe('editor_frame', () => { query: { query: '', language: 'lucene' }, filters: [], }, - title: 'New visualization', + title: 'Untitled', type: 'lens', visualizationType: 'testVis', }, @@ -1578,7 +1578,7 @@ describe('editor_frame', () => { query: { query: 'new query', language: 'lucene' }, filters: [], }, - title: 'New visualization', + title: 'Untitled', type: 'lens', visualizationType: 'testVis', }, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts index 5104ace7c79a3..2f31353d1b5a0 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts @@ -113,7 +113,7 @@ export const getInitialState = (props: EditorFrameProps): EditorFrameState => { } return { - title: i18n.translate('xpack.lens.chartTitle', { defaultMessage: 'New visualization' }), + title: i18n.translate('xpack.lens.chartTitle', { defaultMessage: 'Untitled' }), datasourceStates, activeDatasourceId: getInitialDatasourceId(props), visualization: {