diff --git a/src/plugins/console/common/text_object.ts b/src/plugins/console/common/text_object.ts index 26fa59ec437cc2..2fb6830f260da2 100644 --- a/src/plugins/console/common/text_object.ts +++ b/src/plugins/console/common/text_object.ts @@ -34,5 +34,5 @@ export interface TextObject { * * Used to re-populate a text editor buffer. */ - text: string; + text: string | undefined; } diff --git a/src/plugins/console/public/application/containers/config/config.tsx b/src/plugins/console/public/application/containers/config/config.tsx index 503fdbd9c73545..8605a099182bac 100644 --- a/src/plugins/console/public/application/containers/config/config.tsx +++ b/src/plugins/console/public/application/containers/config/config.tsx @@ -8,16 +8,20 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + useIsWithinBreakpoints, +} from '@elastic/eui'; import { Settings } from './settings'; import { Variables } from './variables'; -export interface Props { - isVerticalLayout: boolean; -} +export function Config() { + const isVerticalLayout = useIsWithinBreakpoints(['xs', 's', 'm']); -export function Config({ isVerticalLayout }: Props) { return ( void; } -export const Editor = memo( - ({ loading, isVerticalLayout, inputEditorValue, setInputEditorValue }: Props) => { - const { - services: { storage, objectStorageClient }, - } = useServicesContext(); +export const Editor = memo(({ loading, inputEditorValue, setInputEditorValue }: Props) => { + const { + services: { storage, objectStorageClient }, + } = useServicesContext(); - const editorValueRef = useRef(null); - const { currentTextObject } = useEditorReadContext(); - const { - requestInFlight, - lastResult: { data: requestData, error: requestError }, - } = useRequestReadContext(); + const { currentTextObject } = useEditorReadContext(); - const dispatch = useRequestActionContext(); - const editorDispatch = useEditorActionContext(); + const { + requestInFlight, + lastResult: { data: requestData, error: requestError }, + } = useRequestReadContext(); - const [fetchingAutocompleteEntities, setFetchingAutocompleteEntities] = useState(false); + const dispatch = useRequestActionContext(); + const editorDispatch = useEditorActionContext(); - useEffect(() => { - const debouncedSetFechingAutocompleteEntities = debounce( - setFetchingAutocompleteEntities, - DEBOUNCE_DELAY - ); - const subscription = getAutocompleteInfo().isLoading$.subscribe( - debouncedSetFechingAutocompleteEntities - ); + const [fetchingAutocompleteEntities, setFetchingAutocompleteEntities] = useState(false); - return () => { - subscription.unsubscribe(); - debouncedSetFechingAutocompleteEntities.cancel(); - }; - }, []); + useEffect(() => { + const debouncedSetFechingAutocompleteEntities = debounce( + setFetchingAutocompleteEntities, + DEBOUNCE_DELAY + ); + const subscription = getAutocompleteInfo().isLoading$.subscribe( + debouncedSetFechingAutocompleteEntities + ); - const [firstPanelSize, secondPanelSize] = storage.get(StorageKeys.SIZE, [ - INITIAL_PANEL_SIZE, - INITIAL_PANEL_SIZE, - ]); + return () => { + subscription.unsubscribe(); + debouncedSetFechingAutocompleteEntities.cancel(); + }; + }, []); - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - const onPanelSizeChange = useCallback( - debounce((sizes) => { - storage.set(StorageKeys.SIZE, Object.values(sizes)); - }, 300), - [] - ); + const [firstPanelSize, secondPanelSize] = storage.get(StorageKeys.SIZE, [ + INITIAL_PANEL_SIZE, + INITIAL_PANEL_SIZE, + ]); - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - const debouncedUpdateLocalStorageValue = useCallback( - debounce((textObject: TextObject) => { - editorValueRef.current = textObject; - objectStorageClient.text.update(textObject); - }, DEBOUNCE_DELAY), - [] - ); + const isVerticalLayout = useIsWithinBreakpoints(['xs', 's', 'm']); - useEffect(() => { - return () => { - editorDispatch({ - type: 'setCurrentTextObject', - payload: editorValueRef.current!, - }); - }; - }, [editorDispatch]); + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + const onPanelSizeChange = useCallback( + debounce((sizes) => { + storage.set(StorageKeys.SIZE, Object.values(sizes)); + }, 300), + [] + ); - // Always keep the localstorage in sync with the value in the editor - // to avoid losing the text object when the user navigates away from the shell - useEffect(() => { - // Only update when its not empty, this is to avoid setting the localstorage value - // to an empty string that will then be replaced by the example request. - if (inputEditorValue !== '') { - const textObject = { - ...currentTextObject, - text: inputEditorValue, - updatedAt: Date.now(), - } as TextObject; + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + const debouncedUpdateLocalStorageValue = useCallback( + debounce((newValue: string | undefined) => { + const textObject = { + ...currentTextObject, + text: newValue, + updatedAt: Date.now(), + } as TextObject; - debouncedUpdateLocalStorageValue(textObject); - } - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [inputEditorValue, debouncedUpdateLocalStorageValue]); + objectStorageClient.text.update(textObject); - const data = getResponseWithMostSevereStatusCode(requestData) ?? requestError; - const isLoading = loading || requestInFlight; + editorDispatch({ + type: 'setCurrentTextObject', + payload: textObject, + }); + }, DEBOUNCE_DELAY), + [] + ); - if (!currentTextObject) return null; + // Always keep the localstorage value in sync with the value in the editor + // to avoid losing the text object when the user navigates away from the shell + useEffect(() => { + debouncedUpdateLocalStorageValue(inputEditorValue); + }, [debouncedUpdateLocalStorageValue, inputEditorValue]); - return ( - <> - {fetchingAutocompleteEntities ? ( -
- -
- ) : null} - onPanelSizeChange(sizes)} - data-test-subj="consoleEditorContainer" - > - {(EuiResizablePanel, EuiResizableButton) => ( - <> - + {fetchingAutocompleteEntities ? ( +
+ +
+ ) : null} + onPanelSizeChange(sizes)} + data-test-subj="consoleEditorContainer" + > + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + - + {loading ? ( + + ) : ( + + )} + + + {!loading && ( - {loading ? ( - - ) : ( - - )} - - - {!loading && ( - { + setInputEditorValue(''); }} - className="consoleEditorPanel" > - setInputEditorValue('')} - > - {i18n.translate('console.editor.clearConsoleInputButton', { - defaultMessage: 'Clear this input', - })} - - - )} - - + {i18n.translate('console.editor.clearConsoleInputButton', { + defaultMessage: 'Clear this input', + })} + + + )} + +
- + - - + + + {data ? ( + + ) : isLoading ? ( + + ) : ( + + )} + + + {(data || isLoading) && ( - {data ? ( - - ) : isLoading ? ( - - ) : ( - - )} - + + + dispatch({ type: 'cleanRequest', payload: undefined })} + > + {i18n.translate('console.editor.clearConsoleOutputButton', { + defaultMessage: 'Clear this output', + })} + + - {(data || isLoading) && ( - - - - dispatch({ type: 'cleanRequest', payload: undefined })} - > - {i18n.translate('console.editor.clearConsoleOutputButton', { - defaultMessage: 'Clear this output', - })} - - - - - - - - - )} - - - - )} -
- - ); - } -); + + + + + + )} + + + + )} + + + ); +}); diff --git a/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts index 961ea586bc2917..9e96c1af2734bc 100644 --- a/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts +++ b/src/plugins/console/public/application/containers/editor/hooks/use_set_initial_value.ts @@ -61,7 +61,7 @@ export const useSetInitialValue = (params: SetInitialValueParams) => { if (parsedURL.origin === 'https://www.elastic.co') { const resp = await fetch(parsedURL); const data = await resp.text(); - setValue(`${localStorageValue}\n\n${data}`); + setValue(`${localStorageValue ?? ''}\n\n${data}`); } else { toasts.addWarning( i18n.translate('console.monaco.loadFromDataUnrecognizedUrlErrorMessage', { @@ -107,7 +107,8 @@ export const useSetInitialValue = (params: SetInitialValueParams) => { if (loadFromParam) { loadBufferFromRemote(loadFromParam); } else { - setValue(localStorageValue || DEFAULT_INPUT_VALUE); + // Only set to default input value if the localstorage value is undefined + setValue(localStorageValue ?? DEFAULT_INPUT_VALUE); } return () => { diff --git a/src/plugins/console/public/application/containers/history/history.tsx b/src/plugins/console/public/application/containers/history/history.tsx index 93777a364356ef..f6d18d6d06dd1c 100644 --- a/src/plugins/console/public/application/containers/history/history.tsx +++ b/src/plugins/console/public/application/containers/history/history.tsx @@ -26,6 +26,7 @@ import { EuiFormFieldset, EuiCheckableCard, EuiResizableContainer, + useIsWithinBreakpoints, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -77,11 +78,7 @@ const CheckeableCardLabel = ({ historyItem }: { historyItem: HistoryProps }) => ); }; -interface Props { - isVerticalLayout: boolean; -} - -export function History({ isVerticalLayout }: Props) { +export function History() { const { euiTheme } = useEuiTheme(); const { services: { history, routeHistory }, @@ -99,6 +96,8 @@ export function History({ isVerticalLayout }: Props) { const [viewingReq, setViewingReq] = useState(null); + const isVerticalLayout = useIsWithinBreakpoints(['xs', 's', 'm']); + const initialize = useCallback(() => { const nextSelectedIndex = 0; setViewingReq(requests[nextSelectedIndex]); diff --git a/src/plugins/console/public/application/containers/main/main.tsx b/src/plugins/console/public/application/containers/main/main.tsx index ef55f5406edaf7..1b5116b2307a67 100644 --- a/src/plugins/console/public/application/containers/main/main.tsx +++ b/src/plugins/console/public/application/containers/main/main.tsx @@ -18,7 +18,6 @@ import { EuiButtonEmpty, EuiHorizontalRule, EuiScreenReaderOnly, - useIsWithinBreakpoints, useEuiOverflowScroll, useEuiTheme, } from '@elastic/eui'; @@ -84,8 +83,6 @@ export function Main({ currentTabProp, isEmbeddable = false }: MainProps) { services: { notifications, routeHistory }, } = useServicesContext(); - const isVerticalLayout = useIsWithinBreakpoints(['xs', 's', 'm']); - const storageTourState = localStorage.getItem(TOUR_STORAGE_KEY); const initialTourState = storageTourState ? JSON.parse(storageTourState) : INITIAL_TOUR_CONFIG; const [tourStepProps, actions, tourState] = useEuiTour(getTourSteps(docLinks), initialTourState); @@ -186,6 +183,8 @@ export function Main({ currentTabProp, isEmbeddable = false }: MainProps) { ); } + if (!currentTextObject) return null; + const shortcutsButton = ( )} - {currentTab === HISTORY_TAB_ID && } - {currentTab === CONFIG_TAB_ID && } + {currentTab === HISTORY_TAB_ID && } + {currentTab === CONFIG_TAB_ID && } { const newObject = await objectStorageClient.text.create({ createdAt: Date.now(), updatedAt: Date.now(), - text: '', + text: undefined, }); dispatch({ type: 'setCurrentTextObject', payload: newObject }); } else { - dispatch({ - type: 'setCurrentTextObject', - // For backwards compatibility, we sort here according to date created to - // always take the first item created. - payload: results.sort((a, b) => a.createdAt - b.createdAt)[0], - }); + // For backwards compatibility, we sort here according to date created to + // always take the first item created. + const lastObject = results.sort((a, b) => a.createdAt - b.createdAt)[0]; + if (lastObject.text === '') { + // If the last stored text is empty, add a new object with undefined text so that the default input is displayed at initial render + const textObject = { + ...lastObject, + text: undefined, + updatedAt: Date.now(), + } as TextObject; + + objectStorageClient.text.update(textObject); + dispatch({ type: 'setCurrentTextObject', payload: textObject }); + } else { + dispatch({ + type: 'setCurrentTextObject', + payload: lastObject, + }); + } } } catch (e) { setError(e); diff --git a/src/plugins/console/tsconfig.json b/src/plugins/console/tsconfig.json index a13333dfa8b301..2b0f6127cd4af7 100644 --- a/src/plugins/console/tsconfig.json +++ b/src/plugins/console/tsconfig.json @@ -33,7 +33,7 @@ "@kbn/react-kibana-mount", "@kbn/ui-theme", "@kbn/core-doc-links-browser", - "@kbn/shared-ux-router" + "@kbn/shared-ux-router", ], "exclude": [ "target/**/*",