diff --git a/src/legacy/core_plugins/console/public/console.js b/src/legacy/core_plugins/console/public/console.js index f5ba87722e1e..2cb8d299d993 100644 --- a/src/legacy/core_plugins/console/public/console.js +++ b/src/legacy/core_plugins/console/public/console.js @@ -27,9 +27,6 @@ require('ui/capabilities/route_setup'); require('./src/controllers/sense_controller'); require('./src/directives/sense_history'); -require('./src/directives/sense_settings'); -require('./src/directives/sense_help'); -require('./src/directives/sense_welcome'); require('./src/directives/console_menu_directive'); diff --git a/src/legacy/core_plugins/console/public/index.html b/src/legacy/core_plugins/console/public/index.html index 71e7d6a04424..76a9f5bc42b5 100644 --- a/src/legacy/core_plugins/console/public/index.html +++ b/src/legacy/core_plugins/console/public/index.html @@ -1,4 +1,6 @@ + +
    @@ -32,3 +34,6 @@
    +
    +
    +
    \ No newline at end of file diff --git a/src/legacy/core_plugins/console/public/src/console_menu.js b/src/legacy/core_plugins/console/public/src/components/console_menu.js similarity index 100% rename from src/legacy/core_plugins/console/public/src/console_menu.js rename to src/legacy/core_plugins/console/public/src/components/console_menu.js diff --git a/src/legacy/core_plugins/console/public/src/directives/sense_help.js b/src/legacy/core_plugins/console/public/src/components/dev_tools_settings.ts similarity index 78% rename from src/legacy/core_plugins/console/public/src/directives/sense_help.js rename to src/legacy/core_plugins/console/public/src/components/dev_tools_settings.ts index 32149aacac38..f3fd1442ca90 100644 --- a/src/legacy/core_plugins/console/public/src/directives/sense_help.js +++ b/src/legacy/core_plugins/console/public/src/components/dev_tools_settings.ts @@ -17,14 +17,14 @@ * under the License. */ -require('./sense_help_example'); -import template from './help.html'; - -require('ui/modules') - .get('app/sense') - .directive('senseHelp', function () { - return { - restrict: 'E', - template - }; - }); +export interface DevToolsSettings { + fontSize: number; + wrapMode: boolean; + autocomplete: { + fields: boolean; + indices: boolean; + templates: boolean; + }; + polling: boolean; + tripleQuotes: boolean; +} diff --git a/src/legacy/core_plugins/console/public/src/directives/sense_help_example.js b/src/legacy/core_plugins/console/public/src/components/editor_example.tsx similarity index 57% rename from src/legacy/core_plugins/console/public/src/directives/sense_help_example.js rename to src/legacy/core_plugins/console/public/src/components/editor_example.tsx index d27c4b39041e..c67b2a364457 100644 --- a/src/legacy/core_plugins/console/public/src/directives/sense_help_example.js +++ b/src/legacy/core_plugins/console/public/src/components/editor_example.tsx @@ -17,25 +17,31 @@ * under the License. */ -const SenseEditor = require('../sense_editor/editor'); +import React, { useEffect } from 'react'; +// @ts-ignore import exampleText from 'raw-loader!./helpExample.txt'; -import { applyResizeCheckerToEditors } from '../sense_editor_resize'; +import $ from 'jquery'; +// @ts-ignore +import SenseEditor from '../sense_editor/editor'; -require('ui/modules') - .get('app/sense') - .directive('senseHelpExample', function () { - return { - restrict: 'E', - link: function ($scope, $el) { - $el.text(exampleText.trim()); - $scope.editor = new SenseEditor($el); - applyResizeCheckerToEditors($scope, $el, $scope.editor); - $scope.editor.setReadOnly(true); - $scope.editor.$blockScrolling = Infinity; +interface EditorExampleProps { + panel: string; +} - $scope.$on('$destroy', function () { - if ($scope.editor) $scope.editor.destroy(); - }); - } +export function EditorExample(props: EditorExampleProps) { + const elemId = `help-example-${props.panel}`; + + useEffect(() => { + const el = $(`#${elemId}`); + el.text(exampleText.trim()); + const editor = new SenseEditor(el); + editor.setReadOnly(true); + editor.$blockScrolling = Infinity; + + return () => { + editor.destroy(); }; - }); + }, []); + + return
    ; +} diff --git a/src/legacy/core_plugins/console/public/src/directives/helpExample.txt b/src/legacy/core_plugins/console/public/src/components/helpExample.txt similarity index 100% rename from src/legacy/core_plugins/console/public/src/directives/helpExample.txt rename to src/legacy/core_plugins/console/public/src/components/helpExample.txt diff --git a/src/legacy/core_plugins/console/public/src/components/help_panel.tsx b/src/legacy/core_plugins/console/public/src/components/help_panel.tsx new file mode 100644 index 000000000000..f17c2a510830 --- /dev/null +++ b/src/legacy/core_plugins/console/public/src/components/help_panel.tsx @@ -0,0 +1,144 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiText, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTitle, + EuiSpacer, +} from '@elastic/eui'; +import { EditorExample } from './editor_example'; + +interface Props { + onClose: () => void; +} + +export function HelpPanel(props: Props) { + return ( + + + +

    + +

    +
    +
    + + +

    + +

    +

    + +

    + +

    + +

    + +
    +
    Ctrl/Cmd + I
    +
    + +
    +
    Ctrl/Cmd + /
    +
    + +
    +
    Ctrl + Space
    +
    + +
    +
    Ctrl/Cmd + Enter
    +
    + +
    +
    Ctrl/Cmd + Up/Down
    +
    + +
    +
    Ctrl/Cmd + Alt + L
    +
    + +
    +
    Ctrl/Cmd + Option + 0
    +
    + +
    +
    Down arrow
    +
    + +
    +
    Enter/Tab
    +
    + +
    +
    Esc
    +
    + +
    +
    +
    +
    +
    + ); +} diff --git a/src/legacy/core_plugins/console/public/src/components/settings_modal.tsx b/src/legacy/core_plugins/console/public/src/components/settings_modal.tsx new file mode 100644 index 000000000000..ac540f18bff0 --- /dev/null +++ b/src/legacy/core_plugins/console/public/src/components/settings_modal.tsx @@ -0,0 +1,249 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiFieldNumber, + EuiFormRow, + EuiCheckboxGroup, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiOverlayMask, + EuiSwitch, +} from '@elastic/eui'; +import { DevToolsSettings } from './dev_tools_settings'; + +export type AutocompleteOptions = 'fields' | 'indices' | 'templates'; + +interface Props { + onSaveSettings: (newSettings: DevToolsSettings) => Promise; + onClose: () => void; + refreshAutocompleteSettings: () => void; + settings: DevToolsSettings; +} + +export function DevToolsSettingsModal(props: Props) { + const [fontSize, setFontSize] = useState(props.settings.fontSize); + const [wrapMode, setWrapMode] = useState(props.settings.wrapMode); + const [fields, setFields] = useState(props.settings.autocomplete.fields); + const [indices, setIndices] = useState(props.settings.autocomplete.indices); + const [templates, setTemplates] = useState(props.settings.autocomplete.templates); + const [polling, setPolling] = useState(props.settings.polling); + const [tripleQuotes, setTripleQuotes] = useState(props.settings.tripleQuotes); + + const autoCompleteCheckboxes = [ + { + id: 'fields', + label: i18n.translate('console.settingsPage.fieldsLabelText', { + defaultMessage: 'Fields', + }), + stateSetter: setFields, + }, + { + id: 'indices', + label: i18n.translate('console.settingsPage.indicesAndAliasesLabelText', { + defaultMessage: 'Indices & Aliases', + }), + stateSetter: setIndices, + }, + { + id: 'templates', + label: i18n.translate('console.settingsPage.templatesLabelText', { + defaultMessage: 'Templates', + }), + stateSetter: setTemplates, + }, + ]; + + const checkboxIdToSelectedMap = { + fields, + indices, + templates, + }; + + const onAutocompleteChange = (optionId: AutocompleteOptions) => { + const option = _.find(autoCompleteCheckboxes, item => item.id === optionId); + if (option) { + option.stateSetter(!checkboxIdToSelectedMap[optionId]); + } + }; + + function saveSettings() { + props.onSaveSettings({ + fontSize, + wrapMode, + autocomplete: { + fields, + indices, + templates, + }, + polling, + tripleQuotes, + }); + } + + return ( + + + + + + + + + + + } + > + { + const val = parseInt(e.target.value, 10); + if (!val) return; + setFontSize(val); + }} + /> + + + + } + onChange={e => setWrapMode(e.target.checked)} + /> + + + + } + > + + } + onChange={e => setTripleQuotes(e.target.checked)} + /> + + + } + > + { + onAutocompleteChange(e as AutocompleteOptions); + }} + /> + + + } + helpText={ + + } + > + + } + onChange={e => setPolling(e.target.checked)} + /> + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/legacy/core_plugins/console/public/src/components/welcome_panel.tsx b/src/legacy/core_plugins/console/public/src/components/welcome_panel.tsx new file mode 100644 index 000000000000..d8e7f75d9929 --- /dev/null +++ b/src/legacy/core_plugins/console/public/src/components/welcome_panel.tsx @@ -0,0 +1,134 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +// @ts-ignore +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTitle, + EuiButton, + EuiText, + EuiFlyoutFooter, +} from '@elastic/eui'; +import { EditorExample } from './editor_example'; + +interface Props { + onDismiss: () => void; +} + +export function WelcomePanel(props: Props) { + return ( + + + +

    + +

    +
    +
    + + +

    + +

    +

    + +

    +

    + +

    + +

    + +

    +

    + +

    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    + + + + + +
    + ); +} diff --git a/src/legacy/core_plugins/console/public/src/controllers/sense_controller.js b/src/legacy/core_plugins/console/public/src/controllers/sense_controller.js index ce39a005750b..003458868f24 100644 --- a/src/legacy/core_plugins/console/public/src/controllers/sense_controller.js +++ b/src/legacy/core_plugins/console/public/src/controllers/sense_controller.js @@ -23,10 +23,15 @@ import $ from 'jquery'; import { initializeInput } from '../input'; import { initializeOutput } from '../output'; import init from '../app'; -import { SenseTopNavController } from './sense_top_nav_controller'; import { getEndpointFromPosition } from '../autocomplete'; import { DOC_LINK_VERSION } from 'ui/documentation_links'; +// welcome message +import { showWelcomePanel } from '../helpers/welcome_show_panel'; +import storage from '../storage'; + +import { getTopNavConfig } from '../helpers/get_top_nav'; + const module = require('ui/modules').get('app/sense'); module.run(function ($rootScope) { @@ -35,10 +40,19 @@ module.run(function ($rootScope) { }; }); -module.controller('SenseController', function SenseController(Private, $scope, $timeout, $location, kbnUiAceKeyboardModeService) { +function showWelcomeMessageIfNeeded($scope) { + if (storage.get('version_welcome_shown') !== '@@SENSE_REVISION') { + const hideWelcomePanel = showWelcomePanel(); + $scope.$on('$destroy', () => { + hideWelcomePanel(); + }); + } +} + +module.controller('SenseController', function SenseController($scope, $timeout, $location, kbnUiAceKeyboardModeService) { docTitle.change('Console'); - $scope.topNavController = Private(SenseTopNavController); + showWelcomeMessageIfNeeded($scope); // Since we pass this callback via reactDirective into a react component, which has the function defined as required // in it's prop types, we should set this initially (before it's set in the $timeout below). Without this line @@ -85,6 +99,15 @@ module.controller('SenseController', function SenseController(Private, $scope, $ } }); }; + + $scope.showHistory = false; + $scope.historyDirty = undefined; + $scope.toggleHistory = () => { + $scope.showHistory = !$scope.showHistory; + }; + + $scope.topNavMenu = getTopNavConfig($scope, $scope.toggleHistory); + $scope.openDocumentation = () => { if (!$scope.documentation) { return; @@ -94,7 +117,11 @@ module.controller('SenseController', function SenseController(Private, $scope, $ $scope.sendSelected = () => { input.focus(); - input.sendCurrentRequestToES(); + input.sendCurrentRequestToES(() => { + // History watches this value and will re-render itself when it changes, so that + // the list of requests stays up-to-date as new requests are sent. + $scope.lastRequestTimestamp = new Date().getTime(); + }); return false; }; diff --git a/src/legacy/core_plugins/console/public/src/directives/_history.scss b/src/legacy/core_plugins/console/public/src/directives/_history.scss index 982669b44656..0d1e9315c397 100644 --- a/src/legacy/core_plugins/console/public/src/directives/_history.scss +++ b/src/legacy/core_plugins/console/public/src/directives/_history.scss @@ -1,3 +1,7 @@ +sense-history { + padding: $euiSizeS; +} + .conHistory__body { display: flex; height: $euiSizeXL * 10; diff --git a/src/legacy/core_plugins/console/public/src/directives/console_menu_directive.js b/src/legacy/core_plugins/console/public/src/directives/console_menu_directive.js index b5a1476e8f9c..be04e6bcc17f 100644 --- a/src/legacy/core_plugins/console/public/src/directives/console_menu_directive.js +++ b/src/legacy/core_plugins/console/public/src/directives/console_menu_directive.js @@ -23,7 +23,7 @@ import { wrapInI18nContext } from 'ui/i18n'; import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/sense', ['react']); -import { ConsoleMenu } from '../console_menu'; +import { ConsoleMenu } from '../components/console_menu'; module.directive('consoleMenu', function (reactDirective) { return reactDirective(wrapInI18nContext(ConsoleMenu)); diff --git a/src/legacy/core_plugins/console/public/src/directives/help.html b/src/legacy/core_plugins/console/public/src/directives/help.html deleted file mode 100644 index 11cb5c254b9a..000000000000 --- a/src/legacy/core_plugins/console/public/src/directives/help.html +++ /dev/null @@ -1,95 +0,0 @@ -

    - -
    - -
    -
    -
    -

    -
    - - -
    -
    -

    -
    -
    -
    Ctrl/Cmd + I
    -
    -
    Ctrl/Cmd + /
    -
    -
    Ctrl + Space
    -
    -
    Ctrl/Cmd + Enter
    -
    -
    Ctrl/Cmd + Up/Down
    -
    -
    Ctrl/Cmd + Alt + L
    -
    -
    Ctrl/Cmd + Option + 0
    -
    -
    Down arrow
    -
    -
    Enter/Tab
    -
    -
    Esc
    -
    -
    -
    -
    -
    diff --git a/src/legacy/core_plugins/console/public/src/directives/sense_history.js b/src/legacy/core_plugins/console/public/src/directives/sense_history.js index 178fe6e7c5dc..dfcee2b87ba7 100644 --- a/src/legacy/core_plugins/console/public/src/directives/sense_history.js +++ b/src/legacy/core_plugins/console/public/src/directives/sense_history.js @@ -33,35 +33,48 @@ require('ui/modules') restrict: 'E', template, controllerAs: 'history', + scope: { + isShown: '=', + historyDirty: '=', + }, controller: function ($scope, $element) { - this.reqs = history.getHistory(); - this.selectedIndex = 0; - this.selectedReq = this.reqs[this.selectedIndex]; - this.viewingReq = this.selectedReq; - - // calculate the text description of a request - this.describeReq = memoize((req) => { - const endpoint = req.endpoint; - const date = moment(req.time); - - let formattedDate = date.format('MMM D'); - if (date.diff(moment(), 'days') > -7) { - formattedDate = date.fromNow(); - } - - return `${endpoint} (${formattedDate})`; + $scope.$watch('historyDirty', () => { + this.init(); }); - this.describeReq.cache = new WeakMap(); + + $scope.$watch('isShown', () => { + if ($scope.isShown) this.init(); + }); + + this.init = () => { + this.reqs = history.getHistory(); + this.selectedIndex = 0; + this.selectedReq = this.reqs[this.selectedIndex]; + this.viewingReq = this.selectedReq; + + // calculate the text description of a request + this.describeReq = memoize((req) => { + const endpoint = req.endpoint; + const date = moment(req.time); + + let formattedDate = date.format('MMM D'); + if (date.diff(moment(), 'days') > -7) { + formattedDate = date.fromNow(); + } + + return `${endpoint} (${formattedDate})`; + }); + this.describeReq.cache = new WeakMap(); + }; // main actions this.clear = () => { history.clearHistory($element); - $scope.kbnTopNav.close(); + this.init(); }; this.restore = (req = this.selectedReq) => { history.restoreFromHistory(req); - $scope.kbnTopNav.close(); }; this.onKeyDown = (ev) => { diff --git a/src/legacy/core_plugins/console/public/src/directives/sense_settings.js b/src/legacy/core_plugins/console/public/src/directives/sense_settings.js deleted file mode 100644 index 9337d6f6e21f..000000000000 --- a/src/legacy/core_plugins/console/public/src/directives/sense_settings.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -require('ui/directives/input_focus'); - -import template from './settings.html'; -import { getAutocomplete, getCurrentSettings, updateSettings, getPolling } from '../settings'; -import mappings from '../mappings'; - -require('ui/modules') - .get('app/sense') - .directive('senseSettings', function () { - return { - restrict: 'E', - template, - controllerAs: 'settings', - controller: function ($scope, $element) { - this.vals = getCurrentSettings(); - - this.isPollingVisible = () => { - const selectedAutoCompleteOptions = - Object.keys(this.vals.autocomplete).filter(key => this.vals.autocomplete[key]); - return selectedAutoCompleteOptions.length > 0; - }; - - this.refresh = () => { - mappings.retrieveAutoCompleteInfo(); - }; - - this.saveSettings = () => { - const prevSettings = getAutocomplete(); - const prevPolling = getPolling(); - - this.vals = updateSettings(this.vals); - - // We'll only retrieve settings if polling is on. - if (getPolling()) { - // Find which, if any, autocomplete settings have changed. - const settingsDiff = Object.keys(prevSettings).filter(key => prevSettings[key] !== this.vals.autocomplete[key]); - const changedSettings = settingsDiff.reduce((changedSettingsAccum, setting) => { - changedSettingsAccum[setting] = this.vals.autocomplete[setting]; - return changedSettingsAccum; - }, {}); - - const isSettingsChanged = settingsDiff.length > 0; - const isPollingChanged = prevPolling !== getPolling(); - - if (isSettingsChanged) { - // If the user has changed one of the autocomplete settings, then we'll fetch just the - // ones which have changed. - mappings.retrieveAutoCompleteInfo(changedSettings); - } else if (isPollingChanged) { - // If the user has turned polling on, then we'll fetch all selected autocomplete settings. - mappings.retrieveAutoCompleteInfo(); - } - } - - $scope.kbnTopNav.close(); - }; - - const self = this; - - function onEnter(event) { - if (event.which === 13) { - self.saveSettings(); - } - } - - const boundElement = $element.bind('keydown', onEnter); - $scope.$on('$destroy', () => boundElement.unbind('keydown', onEnter)); - }, - }; - }); diff --git a/src/legacy/core_plugins/console/public/src/directives/settings.html b/src/legacy/core_plugins/console/public/src/directives/settings.html deleted file mode 100644 index 52e8ce77d345..000000000000 --- a/src/legacy/core_plugins/console/public/src/directives/settings.html +++ /dev/null @@ -1,213 +0,0 @@ -

    - -
    -
    -
    - -
    - - - - -
    - -
    -
    - -
    - - -
    - -
    -
    - -
    - - - - - - -
    - -
    -
    - -
    - -

    - - - - -
    - -
    - - - -
    -
    diff --git a/src/legacy/core_plugins/console/public/src/directives/welcome.html b/src/legacy/core_plugins/console/public/src/directives/welcome.html deleted file mode 100644 index 29fac7592f2f..000000000000 --- a/src/legacy/core_plugins/console/public/src/directives/welcome.html +++ /dev/null @@ -1,76 +0,0 @@ -
    - -

    - -

    - -

    - -

    - -

    - - -

    -

    - -

    - - -

    -
      -
    • -
    • -
    • -
    • -
    • -
    - - - -
    diff --git a/src/legacy/core_plugins/console/public/src/controllers/sense_top_nav_controller.js b/src/legacy/core_plugins/console/public/src/helpers/get_top_nav.ts similarity index 63% rename from src/legacy/core_plugins/console/public/src/controllers/sense_top_nav_controller.js rename to src/legacy/core_plugins/console/public/src/helpers/get_top_nav.ts index e3c1a1f1c80e..4b9b598da8d4 100644 --- a/src/legacy/core_plugins/console/public/src/controllers/sense_top_nav_controller.js +++ b/src/legacy/core_plugins/console/public/src/helpers/get_top_nav.ts @@ -17,61 +17,57 @@ * under the License. */ -import { KbnTopNavControllerProvider } from 'ui/kbn_top_nav/kbn_top_nav_controller'; import { i18n } from '@kbn/i18n'; -import storage from '../storage'; -export function SenseTopNavController(Private) { - const KbnTopNavController = Private(KbnTopNavControllerProvider); +import { IScope } from 'angular'; +import { showSettingsModal } from './settings_show_modal'; - const controller = new KbnTopNavController([ - { - key: 'welcome', - label: i18n.translate('console.topNav.welcomeTabLabel', { - defaultMessage: 'Welcome' - }), - hideButton: true, - template: ``, - testId: 'consoleWelcomeButton', - }, +// help +import { showHelpPanel } from './help_show_panel'; + +export function getTopNavConfig($scope: IScope, toggleHistory: () => {}) { + return [ { key: 'history', label: i18n.translate('console.topNav.historyTabLabel', { - defaultMessage: 'History' + defaultMessage: 'History', }), description: i18n.translate('console.topNav.historyTabDescription', { defaultMessage: 'History', }), - template: ``, + run: () => { + toggleHistory(); + }, testId: 'consoleHistoryButton', }, { key: 'settings', label: i18n.translate('console.topNav.settingsTabLabel', { - defaultMessage: 'Settings' + defaultMessage: 'Settings', }), description: i18n.translate('console.topNav.settingsTabDescription', { defaultMessage: 'Settings', }), - template: ``, + run: () => { + showSettingsModal(); + }, testId: 'consoleSettingsButton', }, { key: 'help', label: i18n.translate('console.topNav.helpTabLabel', { - defaultMessage: 'Help' + defaultMessage: 'Help', }), description: i18n.translate('console.topNav.helpTabDescription', { defaultMessage: 'Help', }), - template: ``, + run: () => { + const hideHelpPanel = showHelpPanel(); + $scope.$on('$destroy', () => { + hideHelpPanel(); + }); + }, testId: 'consoleHelpButton', }, - ]); - - if (storage.get('version_welcome_shown') !== '@@SENSE_REVISION') { - controller.open('welcome'); - } - - return controller; + ]; } diff --git a/src/legacy/core_plugins/console/public/src/directives/sense_welcome.js b/src/legacy/core_plugins/console/public/src/helpers/help_show_panel.tsx similarity index 55% rename from src/legacy/core_plugins/console/public/src/directives/sense_welcome.js rename to src/legacy/core_plugins/console/public/src/helpers/help_show_panel.tsx index 468901264530..a57fc92e1714 100644 --- a/src/legacy/core_plugins/console/public/src/directives/sense_welcome.js +++ b/src/legacy/core_plugins/console/public/src/helpers/help_show_panel.tsx @@ -17,26 +17,31 @@ * under the License. */ -require('./sense_help_example'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import { I18nContext } from 'ui/i18n'; +import { HelpPanel } from '../components/help_panel'; -import { i18n } from '@kbn/i18n'; -import template from './welcome.html'; +let isOpen = false; -const storage = require('../storage'); +export function showHelpPanel(): () => void { + const onClose = () => { + if (!container) return; + ReactDOM.unmountComponentAtNode(container); + isOpen = false; + }; -require('ui/modules') - .get('app/sense') - .directive('senseWelcome', function () { - return { - restrict: 'E', - template, - link: function ($scope) { - $scope.$on('$destroy', function () { - storage.set('version_welcome_shown', '@@SENSE_REVISION'); - }); - $scope.asWellAsFragmentText = i18n.translate('console.welcomePage.supportedRequestFormatDescription.asWellAsFragmentText', { - defaultMessage: 'as well as' - }); - }, - }; - }); + const container = document.getElementById('consoleHelpPanel'); + + if (container && !isOpen) { + isOpen = true; + const element = ( + + + + ); + ReactDOM.render(element, container); + } + + return onClose; +} diff --git a/src/legacy/core_plugins/console/public/src/helpers/settings_show_modal.tsx b/src/legacy/core_plugins/console/public/src/helpers/settings_show_modal.tsx new file mode 100644 index 000000000000..a88955c0ca8b --- /dev/null +++ b/src/legacy/core_plugins/console/public/src/helpers/settings_show_modal.tsx @@ -0,0 +1,86 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { I18nContext } from 'ui/i18n'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { DevToolsSettingsModal } from '../components/settings_modal'; +import { DevToolsSettings } from '../components/dev_tools_settings'; + +// @ts-ignore +import mappings from '../mappings'; +// @ts-ignore +import { getCurrentSettings, updateSettings } from '../settings'; + +export function showSettingsModal() { + const container = document.getElementById('consoleSettingsModal'); + const curSettings = getCurrentSettings(); + + const refreshAutocompleteSettings = () => { + mappings.retrieveAutoCompleteInfo(); + }; + + const closeModal = () => { + if (!container) return; + ReactDOM.unmountComponentAtNode(container); + container.innerHTML = ''; + }; + + const getAutocompleteDiff = (newSettings: DevToolsSettings, prevSettings: DevToolsSettings) => { + return Object.keys(newSettings.autocomplete).filter(key => { + // @ts-ignore + return prevSettings.autocomplete[key] !== newSettings.autocomplete[key]; + }); + }; + + const fetchAutocompleteSettingsIfNeeded = ( + newSettings: DevToolsSettings, + prevSettings: DevToolsSettings + ) => { + // We'll only retrieve settings if polling is on. + const isPollingChanged = prevSettings.polling !== newSettings.polling; + if (newSettings.polling) { + const autocompleteDiff = getAutocompleteDiff(newSettings, prevSettings); + if (autocompleteDiff.length > 0) { + mappings.retrieveAutoCompleteInfo(newSettings.autocomplete); + } else if (isPollingChanged) { + mappings.retrieveAutoCompleteInfo(); + } + } + }; + + const onSave = async (newSettings: DevToolsSettings) => { + const prevSettings = getCurrentSettings(); + updateSettings(newSettings); + fetchAutocompleteSettingsIfNeeded(newSettings, prevSettings); + closeModal(); + }; + + const element = ( + + + + ); + ReactDOM.render(element, container); +} diff --git a/src/legacy/core_plugins/console/public/src/helpers/welcome_show_panel.tsx b/src/legacy/core_plugins/console/public/src/helpers/welcome_show_panel.tsx new file mode 100644 index 000000000000..60a66babef6e --- /dev/null +++ b/src/legacy/core_plugins/console/public/src/helpers/welcome_show_panel.tsx @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { I18nContext } from 'ui/i18n'; +import { WelcomePanel } from '../components/welcome_panel'; +// @ts-ignore +import storage from '../storage'; + +let isOpen = false; + +export function showWelcomePanel(): () => void { + const onClose = () => { + if (!container) return; + ReactDOM.unmountComponentAtNode(container); + isOpen = false; + }; + + const onDismiss = () => { + storage.set('version_welcome_shown', '@@SENSE_REVISION'); + onClose(); + }; + + const container = document.getElementById('consoleWelcomePanel'); + if (container && !isOpen) { + isOpen = true; + const element = ( + + + + ); + ReactDOM.render(element, container); + } + + return onClose; +} diff --git a/src/legacy/core_plugins/console/public/src/input.js b/src/legacy/core_plugins/console/public/src/input.js index 36a6c52fd17b..86c2efe3a70b 100644 --- a/src/legacy/core_plugins/console/public/src/input.js +++ b/src/legacy/core_plugins/console/public/src/input.js @@ -72,7 +72,7 @@ export function initializeInput($el, $actionsEl, $copyAsCurlEl, output, openDocu let CURRENT_REQ_ID = 0; - function sendCurrentRequestToES() { + function sendCurrentRequestToES(addedToHistoryCb) { const reqId = ++CURRENT_REQ_ID; @@ -139,6 +139,9 @@ export function initializeInput($el, $actionsEl, $copyAsCurlEl, output, openDocu // we have someone on the other side. Add to history history.addToHistory(esPath, esMethod, esData); + if (addedToHistoryCb) { + addedToHistoryCb(); + } let value = xhr.responseText; const mode = modeForContentType(xhr.getAllResponseHeaders('Content-Type') || ''); @@ -212,7 +215,7 @@ export function initializeInput($el, $actionsEl, $copyAsCurlEl, output, openDocu input.commands.addCommand({ name: 'send to elasticsearch', bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' }, - exec: sendCurrentRequestToES + exec: () => sendCurrentRequestToES() }); input.commands.addCommand({ name: 'open documentation', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a53e2ba86a3d..36c1f5226397 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -734,7 +734,6 @@ "console.topNav.historyTabLabel": "履歴", "console.topNav.settingsTabDescription": "設定", "console.topNav.settingsTabLabel": "設定", - "console.topNav.welcomeTabLabel": "ようこそ", "console.welcomePage.closeButtonLabel": "始めましょう", "console.welcomePage.pageTitle": "コンソールへようこそ", "console.welcomePage.quickIntroDescription": "コンソール UI は、エディターペイン (左) と応答ペイン (右) の 2 つのペインに分かれています。エディターでリクエストを入力し、Elasticsearch に送信します。結果が右側の応答ペインに表示されます。", @@ -745,8 +744,6 @@ "console.welcomePage.quickTips.submitRequestDescription": "緑の三角形のボタンをクリックして ES にリクエストを送信します。", "console.welcomePage.quickTips.useWrenchMenuDescription": "レンチメニューで他の便利な機能が使えます。", "console.welcomePage.quickTipsTitle": "今のうちにいくつか簡単なコツをお教えします", - "console.welcomePage.supportedRequestFormatDescription": "リクエストの入力中、コンソールが候補を提案するので、Enter/Tab を押して確定できます。これらの候補はリクエストの構造{asWellAs}インデックス、タイプに基づくものです。", - "console.welcomePage.supportedRequestFormatDescription.asWellAsFragmentText": "や", "console.welcomePage.supportedRequestFormatTitle": "コンソールは cURL と同様に、コンパクトなフォーマットのリクエストを理解できます。", "core.chrome.legacyBrowserWarning": "ご使用のブラウザが Kibana のセキュリティ要件を満たしていません。", "core.euiBasicTable.selectAllRows": "すべての行を選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b6d323fb1802..c76597cb08c8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -601,7 +601,6 @@ "console.topNav.historyTabLabel": "历史记录", "console.topNav.settingsTabDescription": "设置", "console.topNav.settingsTabLabel": "设置", - "console.topNav.welcomeTabLabel": "欢迎", "console.welcomePage.closeButtonLabel": "开始工作", "console.welcomePage.pageTitle": "欢迎使用 Console", "console.welcomePage.quickIntroDescription": "Console UI 分成两个窗格:编辑器窗格(左)和响应窗格(右)。使用编辑器键入请求并将它们提交到 Elasticsearch。结果将显示在右侧的响应窗格中。", @@ -612,8 +611,6 @@ "console.welcomePage.quickTips.submitRequestDescription": "使用绿色三角按钮将请求提交到 ES。", "console.welcomePage.quickTips.useWrenchMenuDescription": "使用扳手菜单执行其他有用的操作。", "console.welcomePage.quickTipsTitle": "有几个需要您注意的有用提示", - "console.welcomePage.supportedRequestFormatDescription": "键入请求时,Console 将提供建议,您可以通过按 Enter/Tab 键来接受建议。这些建议基于请求结构{asWellAs}您的索引和类型做出。", - "console.welcomePage.supportedRequestFormatDescription.asWellAsFragmentText": "以及", "console.welcomePage.supportedRequestFormatTitle": "Console 理解紧凑格式的请求,类似于 cURL:", "data.query.queryBar.searchInputPlaceholder": "搜索……(例如,status:200 AND extension:PHP)", "data.query.queryBar.syntaxOptionsDescription": "我们的实验性自动完成功能及简单语法功能可以帮助您创建自己的查询。只需开始键入,便会看到与您的数据相关的匹配。请参阅{docsLink}的文档。",