diff --git a/packages/kbn-search-api-panels/components/code_box.tsx b/packages/kbn-search-api-panels/components/code_box.tsx index 21c4085f44a9b..5a4ff7cc13240 100644 --- a/packages/kbn-search-api-panels/components/code_box.tsx +++ b/packages/kbn-search-api-panels/components/code_box.tsx @@ -23,6 +23,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ApplicationStart } from '@kbn/core-application-browser'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { LanguageDefinition } from '../types'; @@ -38,6 +39,7 @@ interface CodeBoxProps { setSelectedLanguage: (language: LanguageDefinition) => void; assetBasePath: string; application?: ApplicationStart; + consolePlugin?: ConsolePluginStart; sharePlugin: SharePluginStart; consoleRequest?: string; } @@ -45,6 +47,7 @@ interface CodeBoxProps { export const CodeBox: React.FC = ({ application, codeSnippet, + consolePlugin, languageType, languages, assetBasePath, @@ -117,6 +120,7 @@ export const CodeBox: React.FC = ({ diff --git a/packages/kbn-search-api-panels/components/ingest_data.tsx b/packages/kbn-search-api-panels/components/ingest_data.tsx index f8ba59d29bf30..0700d2d56d661 100644 --- a/packages/kbn-search-api-panels/components/ingest_data.tsx +++ b/packages/kbn-search-api-panels/components/ingest_data.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { ApplicationStart } from '@kbn/core-application-browser'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import { CodeBox } from './code_box'; import { LanguageDefinition } from '../types'; @@ -26,6 +27,7 @@ interface IngestDataProps { }; assetBasePath: string; application?: ApplicationStart; + consolePlugin?: ConsolePluginStart; sharePlugin: SharePluginStart; languages: LanguageDefinition[]; consoleRequest?: string; @@ -39,6 +41,7 @@ export const IngestData: React.FC = ({ docLinks, assetBasePath, application, + consolePlugin, sharePlugin, languages, consoleRequest, @@ -58,6 +61,7 @@ export const IngestData: React.FC = ({ setSelectedLanguage={setSelectedLanguage} assetBasePath={assetBasePath} application={application} + consolePlugin={consolePlugin} sharePlugin={sharePlugin} /> } diff --git a/packages/kbn-search-api-panels/components/try_in_console_button.tsx b/packages/kbn-search-api-panels/components/try_in_console_button.tsx index fe109e025e2e5..7007c306a2cd6 100644 --- a/packages/kbn-search-api-panels/components/try_in_console_button.tsx +++ b/packages/kbn-search-api-panels/components/try_in_console_button.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { EuiButtonEmpty } from '@elastic/eui'; import type { ApplicationStart } from '@kbn/core-application-browser'; import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { compressToEncodedURIComponent } from 'lz-string'; @@ -18,11 +19,13 @@ import { compressToEncodedURIComponent } from 'lz-string'; export interface TryInConsoleButtonProps { request: string; application?: ApplicationStart; + consolePlugin?: ConsolePluginStart; sharePlugin: SharePluginStart; } export const TryInConsoleButton = ({ request, application, + consolePlugin, sharePlugin, }: TryInConsoleButtonProps) => { const { url } = sharePlugin; @@ -39,8 +42,20 @@ export const TryInConsoleButton = ({ ); if (!consolePreviewLink) return null; + const onClick = () => { + const embeddedConsoleAvailable = + (consolePlugin?.openEmbeddedConsole !== undefined && + consolePlugin?.isEmbeddedConsoleAvailable?.()) ?? + false; + if (embeddedConsoleAvailable) { + consolePlugin!.openEmbeddedConsole!(request); + } else { + window.open(consolePreviewLink, '_blank', 'noreferrer'); + } + }; + return ( - + { - const [, queryString] = (window.location.hash || '').split('?'); + const [, queryString] = (window.location.hash || window.location.search || '').split('?'); return parse(queryString || '', { sort: false }) as Required; }; diff --git a/src/plugins/console/public/application/containers/embeddable/console_wrapper.tsx b/src/plugins/console/public/application/containers/embeddable/console_wrapper.tsx index 340b20c91b6cd..3618194e194dc 100644 --- a/src/plugins/console/public/application/containers/embeddable/console_wrapper.tsx +++ b/src/plugins/console/public/application/containers/embeddable/console_wrapper.tsx @@ -14,8 +14,10 @@ import { I18nStart, CoreTheme, DocLinksStart, + CoreStart, } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; +import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { ObjectStorageClient } from '../../../../common/types'; @@ -62,10 +64,10 @@ interface ConsoleDependencies { trackUiMetric: MetricsTracker; } -const loadDependencies = async ({ - core, - usageCollection, -}: EmbeddableConsoleDependencies): Promise => { +const loadDependencies = async ( + core: CoreStart, + usageCollection?: UsageCollectionStart +): Promise => { const { docLinks: { DOC_LINK_VERSION, links }, http, @@ -107,10 +109,12 @@ const loadDependencies = async ({ }; }; -export const ConsoleWrapper = (props: EmbeddableConsoleDependencies): React.ReactElement => { +type ConsoleWrapperProps = Omit; + +export const ConsoleWrapper: React.FunctionComponent = (props) => { const [dependencies, setDependencies] = useState(null); useEffect(() => { - loadDependencies(props).then(setDependencies); + loadDependencies(props.core, props.usageCollection).then(setDependencies); }, [setDependencies, props]); useEffect(() => { return () => { diff --git a/src/plugins/console/public/application/containers/embeddable/embeddable_console.tsx b/src/plugins/console/public/application/containers/embeddable/embeddable_console.tsx index 2577c9d4841d7..2167ec12c52c0 100644 --- a/src/plugins/console/public/application/containers/embeddable/embeddable_console.tsx +++ b/src/plugins/console/public/application/containers/embeddable/embeddable_console.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useState } from 'react'; +import React, { useReducer, useEffect } from 'react'; import classNames from 'classnames'; import useObservable from 'react-use/lib/useObservable'; import { @@ -25,6 +25,9 @@ import { EmbeddableConsoleDependencies, } from '../../../types/embeddable_console'; +import * as store from '../../stores/embeddable_console'; +import { setLoadFromParameter, removeLoadFromParameter } from '../../lib/load_from'; + import { ConsoleWrapper } from './console_wrapper'; import './_index.scss'; @@ -36,10 +39,31 @@ export const EmbeddableConsole = ({ size = 'm', core, usageCollection, + setDispatch, }: EmbeddableConsoleProps & EmbeddableConsoleDependencies) => { - const [isConsoleOpen, setIsConsoleOpen] = useState(false); - const toggleConsole = () => setIsConsoleOpen(!isConsoleOpen); + const [consoleState, consoleDispatch] = useReducer( + store.reducer, + store.initialValue, + (value) => ({ ...value }) + ); const chromeStyle = useObservable(core.chrome.getChromeStyle$()); + useEffect(() => { + setDispatch(consoleDispatch); + return () => setDispatch(null); + }, [setDispatch, consoleDispatch]); + useEffect(() => { + if (consoleState.isOpen && consoleState.loadFromContent) { + setLoadFromParameter(consoleState.loadFromContent); + } else if (!consoleState.isOpen) { + removeLoadFromParameter(); + } + }, [consoleState.isOpen, consoleState.loadFromContent]); + + const isConsoleOpen = consoleState.isOpen; + const setIsConsoleOpen = (value: boolean) => { + consoleDispatch(value ? { type: 'open' } : { type: 'close' }); + }; + const toggleConsole = () => setIsConsoleOpen(!isConsoleOpen); const onKeyDown = (event: any) => { if (event.key === keys.ESCAPE) { diff --git a/src/plugins/console/public/application/lib/load_from.test.ts b/src/plugins/console/public/application/lib/load_from.test.ts new file mode 100644 index 0000000000000..289718f46cf10 --- /dev/null +++ b/src/plugins/console/public/application/lib/load_from.test.ts @@ -0,0 +1,236 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { compressToEncodedURIComponent } from 'lz-string'; +import { setLoadFromParameter, removeLoadFromParameter } from './load_from'; + +const baseMockWindow = () => { + return { + history: { + pushState: jest.fn(), + }, + location: { + host: 'my-kibana.elastic.co', + pathname: '', + protocol: 'https:', + search: '', + hash: '', + }, + }; +}; +let windowSpy: jest.SpyInstance; +let mockWindow = baseMockWindow(); + +describe('load from lib', () => { + beforeEach(() => { + mockWindow = baseMockWindow(); + windowSpy = jest.spyOn(globalThis, 'window', 'get'); + windowSpy.mockImplementation(() => mockWindow); + }); + + afterEach(() => { + windowSpy.mockRestore(); + }); + + describe('setLoadFromParameter', () => { + it('adds load_from as expected', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: '#/console', + }; + const codeSnippet = 'GET /_stats'; + const expectedUrl = + 'https://my-kibana.elastic.co/foo/app/dev_tools#/console?load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgzgFwIYLkA'; + + setLoadFromParameter(codeSnippet); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('can replace an existing value', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: `#/console?load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgxgQwC5QJYDsAmq4FMDOQA`, + }; + const codeSnippet = 'GET /_stats'; + const expectedUrl = + 'https://my-kibana.elastic.co/foo/app/dev_tools#/console?load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgzgFwIYLkA'; + + setLoadFromParameter(codeSnippet); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('works with other query params', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: '#/console?foo=bar', + }; + const codeSnippet = 'GET /_stats'; + const expectedUrl = + 'https://my-kibana.elastic.co/foo/app/dev_tools#/console?foo=bar&load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgzgFwIYLkA'; + + setLoadFromParameter(codeSnippet); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('works with a non-hash route', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/enterprise_search/overview', + }; + const codeSnippet = 'GET /_stats'; + const expectedUrl = + 'https://my-kibana.elastic.co/foo/app/enterprise_search/overview?load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgzgFwIYLkA'; + + setLoadFromParameter(codeSnippet); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('works with a non-hash route and other params', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/enterprise_search/overview', + search: '?foo=bar', + }; + const codeSnippet = 'GET /_stats'; + const expectedUrl = + 'https://my-kibana.elastic.co/foo/app/enterprise_search/overview?foo=bar&load_from=data%3Atext%2Fplain%2COIUQKgBA9A%2BgzgFwIYLkA'; + + setLoadFromParameter(codeSnippet); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + }); + + describe('removeLoadFromParameter', () => { + it('leaves other params in place', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + search: `?foo=bar&load_from=data:text/plain,${compressToEncodedURIComponent( + 'GET /_cat/indices' + )}`, + }; + + const expectedUrl = 'https://my-kibana.elastic.co/foo/app/dev_tools?foo=bar'; + + removeLoadFromParameter(); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('leaves other params with a hashroute', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: `#/console?foo=bar&load_from=data:text/plain,${compressToEncodedURIComponent( + 'GET /_cat/indices' + )}`, + }; + + const expectedUrl = 'https://my-kibana.elastic.co/foo/app/dev_tools#/console?foo=bar'; + + removeLoadFromParameter(); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('removes ? if load_from was the only param', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + search: `?load_from=data:text/plain,${compressToEncodedURIComponent('GET /_cat/indices')}`, + }; + + const expectedUrl = 'https://my-kibana.elastic.co/foo/app/dev_tools'; + + removeLoadFromParameter(); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('removes ? if load_from was the only param in a hashroute', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: `#/console?load_from=data:text/plain,${compressToEncodedURIComponent( + 'GET /_cat/indices' + )}`, + }; + + const expectedUrl = 'https://my-kibana.elastic.co/foo/app/dev_tools#/console'; + + removeLoadFromParameter(); + expect(mockWindow.history.pushState).toHaveBeenCalledTimes(1); + expect(mockWindow.history.pushState).toHaveBeenCalledWith( + { + path: expectedUrl, + }, + '', + expectedUrl + ); + }); + it('noop if load_from not currently defined on QS', () => { + mockWindow.location = { + ...mockWindow.location, + pathname: '/foo/app/dev_tools', + hash: `#/console?foo=bar`, + }; + + removeLoadFromParameter(); + expect(mockWindow.history.pushState).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/plugins/console/public/application/lib/load_from.ts b/src/plugins/console/public/application/lib/load_from.ts new file mode 100644 index 0000000000000..c601eafb6f3a9 --- /dev/null +++ b/src/plugins/console/public/application/lib/load_from.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import qs from 'query-string'; +import { compressToEncodedURIComponent } from 'lz-string'; + +function getBaseUrl() { + return `${window.location.protocol}//${window.location.host}${window.location.pathname}`; +} +function parseQueryString() { + const [hashRoute, queryString] = (window.location.hash || window.location.search || '').split( + '?' + ); + + const parsedQueryString = qs.parse(queryString || '', { sort: false }); + return { + hasHash: !!window.location.hash, + hashRoute, + queryString: parsedQueryString, + }; +} + +export const setLoadFromParameter = (value: string) => { + const baseUrl = getBaseUrl(); + const { hasHash, hashRoute, queryString } = parseQueryString(); + const consoleDataUri = compressToEncodedURIComponent(value); + queryString.load_from = `data:text/plain,${consoleDataUri}`; + const params = `?${qs.stringify(queryString)}`; + const newUrl = hasHash ? `${baseUrl}${hashRoute}${params}` : `${baseUrl}${params}`; + + window.history.pushState({ path: newUrl }, '', newUrl); +}; + +export const removeLoadFromParameter = () => { + const baseUrl = getBaseUrl(); + const { hasHash, hashRoute, queryString } = parseQueryString(); + if (queryString.load_from) { + delete queryString.load_from; + + const params = Object.keys(queryString).length ? `?${qs.stringify(queryString)}` : ''; + const newUrl = hasHash ? `${baseUrl}${hashRoute}${params}` : `${baseUrl}${params}`; + window.history.pushState({ path: newUrl }, '', newUrl); + } +}; diff --git a/src/plugins/console/public/application/stores/embeddable_console.ts b/src/plugins/console/public/application/stores/embeddable_console.ts new file mode 100644 index 0000000000000..4bfb38d094c13 --- /dev/null +++ b/src/plugins/console/public/application/stores/embeddable_console.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Reducer } from 'react'; +import { produce } from 'immer'; +import { identity } from 'fp-ts/lib/function'; + +import { EmbeddedConsoleAction, EmbeddedConsoleStore } from '../../types/embeddable_console'; + +export const initialValue: EmbeddedConsoleStore = produce( + { + isOpen: false, + }, + identity +); + +export const reducer: Reducer = (state, action) => + produce(state, (draft) => { + switch (action.type) { + case 'open': + if (!state.isOpen) { + draft.isOpen = true; + draft.loadFromContent = action.payload?.content; + return; + } + break; + case 'close': + if (state.isOpen) { + draft.isOpen = false; + draft.loadFromContent = undefined; + return; + } + break; + } + return draft; + }); diff --git a/src/plugins/console/public/plugin.ts b/src/plugins/console/public/plugin.ts index 711b4304af9b0..53cb16befe865 100644 --- a/src/plugins/console/public/plugin.ts +++ b/src/plugins/console/public/plugin.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from '@kbn/core/public'; @@ -20,10 +19,12 @@ import { EmbeddableConsoleProps, EmbeddableConsoleDependencies, } from './types'; -import { AutocompleteInfo, setAutocompleteInfo } from './services'; +import { AutocompleteInfo, setAutocompleteInfo, EmbeddableConsoleInfo } from './services'; export class ConsoleUIPlugin implements Plugin { private readonly autocompleteInfo = new AutocompleteInfo(); + private _embeddableConsole: EmbeddableConsoleInfo = new EmbeddableConsoleInfo(); + constructor(private ctx: PluginInitializerContext) {} public setup( @@ -118,9 +119,16 @@ export class ConsoleUIPlugin implements Plugin { + this._embeddableConsole.setDispatch(d); + }, }; return renderEmbeddableConsole(props, consoleDeps); }; + consoleStart.isEmbeddedConsoleAvailable = () => + this._embeddableConsole.isEmbeddedConsoleAvailable(); + consoleStart.openEmbeddedConsole = (content?: string) => + this._embeddableConsole.openEmbeddedConsole(content); } return consoleStart; diff --git a/src/plugins/console/public/services/embeddable_console.test.ts b/src/plugins/console/public/services/embeddable_console.test.ts new file mode 100644 index 0000000000000..7df8230b6dbdf --- /dev/null +++ b/src/plugins/console/public/services/embeddable_console.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EmbeddableConsoleInfo } from './embeddable_console'; + +describe('EmbeddableConsoleInfo', () => { + let eConsole: EmbeddableConsoleInfo; + beforeEach(() => { + eConsole = new EmbeddableConsoleInfo(); + }); + describe('isEmbeddedConsoleAvailable', () => { + it('returns true if dispatch has been set', () => { + eConsole.setDispatch(jest.fn()); + expect(eConsole.isEmbeddedConsoleAvailable()).toBe(true); + }); + it('returns false if dispatch has not been set', () => { + expect(eConsole.isEmbeddedConsoleAvailable()).toBe(false); + }); + it('returns false if dispatch has been cleared', () => { + eConsole.setDispatch(jest.fn()); + eConsole.setDispatch(null); + expect(eConsole.isEmbeddedConsoleAvailable()).toBe(false); + }); + }); + describe('openEmbeddedConsole', () => { + const mockDispatch = jest.fn(); + beforeEach(() => { + jest.clearAllMocks(); + + eConsole.setDispatch(mockDispatch); + }); + it('dispatches open action', () => { + eConsole.openEmbeddedConsole(); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'open' }); + }); + it('can set content', () => { + eConsole.openEmbeddedConsole('GET /_cat/_indices'); + + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith({ + type: 'open', + payload: { content: 'GET /_cat/_indices' }, + }); + }); + }); +}); diff --git a/src/plugins/console/public/services/embeddable_console.ts b/src/plugins/console/public/services/embeddable_console.ts new file mode 100644 index 0000000000000..6bc32b8475eef --- /dev/null +++ b/src/plugins/console/public/services/embeddable_console.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { Dispatch } from 'react'; + +import { EmbeddedConsoleAction as EmbeddableConsoleAction } from '../types/embeddable_console'; + +export class EmbeddableConsoleInfo { + private _dispatch: Dispatch | null = null; + + public setDispatch(d: Dispatch | null) { + this._dispatch = d; + } + + public isEmbeddedConsoleAvailable(): boolean { + return this._dispatch !== null; + } + + public openEmbeddedConsole(content?: string) { + // Embedded Console is not rendered on the page, nothing to do + if (!this._dispatch) return; + + this._dispatch({ type: 'open', payload: content ? { content } : undefined }); + } +} diff --git a/src/plugins/console/public/services/index.ts b/src/plugins/console/public/services/index.ts index 73929f89e386f..669ed890729dc 100644 --- a/src/plugins/console/public/services/index.ts +++ b/src/plugins/console/public/services/index.ts @@ -16,3 +16,4 @@ export { setAutocompleteInfo, ENTITIES, } from './autocomplete'; +export { EmbeddableConsoleInfo } from './embeddable_console'; diff --git a/src/plugins/console/public/types/embeddable_console.ts b/src/plugins/console/public/types/embeddable_console.ts index da0e3346a7bd2..71a755b7dd544 100644 --- a/src/plugins/console/public/types/embeddable_console.ts +++ b/src/plugins/console/public/types/embeddable_console.ts @@ -7,6 +7,7 @@ */ import type { CoreStart } from '@kbn/core/public'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { Dispatch } from 'react'; /** * EmbeddableConsoleProps are optional props used when rendering the embeddable developer console. @@ -21,4 +22,14 @@ export interface EmbeddableConsoleProps { export interface EmbeddableConsoleDependencies { core: CoreStart; usageCollection?: UsageCollectionStart; + setDispatch: (dispatch: Dispatch | null) => void; +} + +export type EmbeddedConsoleAction = + | { type: 'open'; payload?: { content?: string } } + | { type: 'close' }; + +export interface EmbeddedConsoleStore { + isOpen: boolean; + loadFromContent?: string; } diff --git a/src/plugins/console/public/types/plugin_dependencies.ts b/src/plugins/console/public/types/plugin_dependencies.ts index e4f65d44cb727..199e59d4b9b92 100644 --- a/src/plugins/console/public/types/plugin_dependencies.ts +++ b/src/plugins/console/public/types/plugin_dependencies.ts @@ -47,4 +47,14 @@ export interface ConsolePluginStart { * render an embeddable version of the developer console on the page. */ renderEmbeddableConsole?: (props?: EmbeddableConsoleProps) => ReactElement | null; + /** + * isEmbeddedConsoleAvailable is available if the embedded console can be rendered. Returns true when + * called if the Embedded Console is currently rendered. + */ + isEmbeddedConsoleAvailable?: () => boolean; + /** + * openEmbeddedConsole is available if the embedded console can be rendered. Calling + * this function will open the embedded console on the page if it is currently rendered. + */ + openEmbeddedConsole?: (content?: string) => void; } diff --git a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts index 2379692abb736..4ab0cfae0932d 100644 --- a/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts +++ b/x-pack/plugins/enterprise_search/common/types/kibana_deps.ts @@ -7,6 +7,7 @@ import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { CloudStart } from '@kbn/cloud-plugin/public'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DiscoverStart } from '@kbn/discover-plugin/public'; import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; @@ -19,6 +20,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; export interface KibanaDeps { charts: ChartsPluginStart; cloud: CloudStart; + console?: ConsolePluginStart; data: DataPublicPluginStart; discover: DiscoverStart; features: FeaturesPluginStart; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx index e834e9ff45fe5..5d2d8cecf8466 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx @@ -43,6 +43,7 @@ export const AddDataPanelContent: React.FC = ({ setSelectedLanguage={setSelectedLanguage} assetBasePath={assetBasePath} application={services.application} + consolePlugin={services.console} sharePlugin={services.share} /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx index 2ce2801f033e0..d32614865b614 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx @@ -47,6 +47,7 @@ export const SearchQueryPanelContent: React.FC = ( setSelectedLanguage={setSelectedLanguage} assetBasePath={assetBasePath} application={services.application} + consolePlugin={services.console} sharePlugin={services.share} /> ); diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index 2a2313564c2e2..1cb337e6584b9 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -250,6 +250,7 @@ export const ElasticsearchOverview = () => { assetBasePath={assetBasePath} docLinks={docLinks} application={application} + consolePlugin={consolePlugin} sharePlugin={share} additionalIngestionPanel={} /> @@ -277,6 +278,7 @@ export const ElasticsearchOverview = () => { setSelectedLanguage={setSelectedLanguage} assetBasePath={assetBasePath} application={application} + consolePlugin={consolePlugin} sharePlugin={share} /> }