Skip to content

Commit

Permalink
[Console][Serverless Search] Introduce Embeddable Console, Show Embed…
Browse files Browse the repository at this point in the history
…dable Console in Serverless Search (elastic#174643)

## Summary

This PR introduces the `renderEmbeddableConsole` from the console
plugin. This allows other plugin's to embed a portable version of the
dev console on their own pages. Initially the embeddable console is
being added to Serverless Search getting started & connectors pages.
There will be follow-up work to add the embeddable console to other
pages for serverless search. Then again work to add the remote console
to Search and other pages in v8.13.0 of Kibana.

### Screenshots
Closed

![image](https://github.com/elastic/kibana/assets/1972968/8bf69034-ae79-41f7-9161-4baf2b9e7a17)
Open

![image](https://github.com/elastic/kibana/assets/1972968/8fac53c1-997f-4009-b2c2-c085cc8ee9ec)

### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
TattdCodeMonkey and kibanamachine authored Jan 16, 2024
1 parent b19b995 commit 4f4857c
Show file tree
Hide file tree
Showing 20 changed files with 572 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
.embeddableConsole {
background: $embeddableConsoleBackground;
color: $embeddableConsoleText;
display: flex;
flex-direction: column;
// This large box shadow helps prevent a flicker of dark
// background when the content is shown and hidden
box-shadow: inset 0 $embeddableConsoleInitialHeight 0 $embeddableConsoleBackground, inset 0 600rem 0 $euiPageBackgroundColor;
bottom: 0;
right: 0;
left: var(--euiCollapsibleNavOffset, 0);
transform: translateY(0);
height: $embeddableConsoleInitialHeight;
max-height: $embeddableConsoleMaxHeight;

&--fixed {
position: fixed;
z-index: $euiZLevel1;
}

&-isOpen {
animation-duration: $euiAnimSpeedNormal;
animation-timing-function: $euiAnimSlightResistance;
animation-fill-mode: forwards;
}

&-isOpen.embeddableConsole--large {
animation-name: embeddableConsoleOpenPanelLarge;
height: $embeddableConsoleMaxHeight;
bottom: map-get($embeddableConsoleHeights, 'l') * -1;
}

&-isOpen.embeddableConsole--medium {
animation-name: embeddableConsoleOpenPanelMedium;
height: map-get($embeddableConsoleHeights, 'm');
bottom: map-get($embeddableConsoleHeights, 'm') * -1;
}

&-isOpen.embeddableConsole--small {
animation-name: embeddableConsoleOpenPanelSmall;
height: map-get($embeddableConsoleHeights, 's');
bottom: map-get($embeddableConsoleHeights, 's') * -1;
}
}

.embeddableConsole__controls {
height: $embeddableConsoleInitialHeight;
width: 100%;
display: flex;
justify-content: flex-start;
overflow-y: hidden; // Ensures the movement of buttons in :focus don't cause scrollbars
overflow-x: auto;

&--button {
justify-content: flex-start;
}
}

.embeddableConsole__content {
@include euiScrollBar;
overflow-y: auto;
width: 100%;
height: calc(100% - #{$embeddableConsoleInitialHeight});
background-color: $euiPageBackgroundColor;
animation-name: embeddableConsoleShowContent;
animation-duration: $euiAnimSpeedSlow;
animation-iteration-count: 1;
animation-timing-function: $euiAnimSlightResistance;
color: $euiColorDarkestShade;

#consoleRoot {
height: 100%;
}
}

@each $colorName, $colorValue in $euiButtonTypes {
.embeddableConsole__controls {
.euiLink.euiLink--#{$colorName} {
color: makeHighContrastColor($colorValue, $embeddableConsoleBackground);

&:hover {
color: tintOrShade($colorValue, 30%, 30%);
}
}

.euiLink.euiLink--text {
color: $euiColorGhost;
}

.embeddableConsole__button.euiButton[class*='#{$colorName}']:enabled:not(.euiButton--fill) {
color: makeHighContrastColor($colorValue, $embeddableConsoleBackground);
border-color: makeHighContrastColor($colorValue, $embeddableConsoleBackground);
}

.euiButtonIcon[class*='#{$colorName}'] {
color: makeHighContrastColor($colorValue, $embeddableConsoleBackground);
}
}
}

@include euiBreakpoint('xs', 's') {
.embeddableConsole:not(.embeddableConsole--showOnMobile) {
display: none;
}
}

@keyframes embeddableConsoleOpenPanelLarge {
0% {
// Accounts for the initial height offset from the top
transform: translateY(calc((#{$embeddableConsoleInitialHeight} * 3) * -1));
}

100% {
transform: translateY(map-get($embeddableConsoleHeights, 'l') * -1);
}
}

@keyframes embeddableConsoleOpenPanelMedium {
0% {
transform: translateY(-$embeddableConsoleInitialHeight);
}

100% {
transform: translateY(map-get($embeddableConsoleHeights, 'm') * -1);
}
}

@keyframes embeddableConsoleOpenPanelSmall {
0% {
transform: translateY(-$embeddableConsoleInitialHeight);
}

100% {
transform: translateY(map-get($embeddableConsoleHeights, 's') * -1);
}
}

@keyframes embeddableConsoleShowContent {
0% {
opacity: 0;
}

100% {
opacity: 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'variables';
@import 'embeddable_console';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$embeddableConsoleBackground: lightOrDarkTheme($euiColorDarkestShade, $euiColorInk);
$embeddableConsoleText: lighten(makeHighContrastColor($euiColorLightestShade, $embeddableConsoleBackground), 20%);
$embeddableConsoleBorderColor: transparentize($euiColorGhost, .8);
$embeddableConsoleInitialHeight: $euiSizeXXL;
$embeddableConsoleMaxHeight: calc(100vh - #{$euiSize * 5});

// Pixel heights ensure no blurriness caused by half pixel offsets
$embeddableConsoleHeights: (
s: $euiSize * 30,
m: $euiSize * 50,
l: 100vh,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* 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 React, { useMemo, useEffect } from 'react';
import { Observable } from 'rxjs';
import {
HttpSetup,
NotificationsStart,
I18nStart,
CoreTheme,
DocLinksStart,
} from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';

import { ObjectStorageClient } from '../../../../common/types';

import * as localStorageObjectClient from '../../../lib/local_storage_object_client';
import {
getAutocompleteInfo,
AutocompleteInfo,
History,
Settings,
Storage,
createStorage,
createHistory,
createSettings,
setStorage,
} from '../../../services';
import { createUsageTracker } from '../../../services/tracker';
import { MetricsTracker, EmbeddableConsoleDependencies } from '../../../types';

import { createApi, createEsHostService } from '../../lib';
import { EsHostService } from '../../lib/es_host_service';

import {
ServicesContextProvider,
EditorContextProvider,
RequestContextProvider,
} from '../../contexts';
import { Main } from '../main';

interface ConsoleDependencies {
I18nContext: I18nStart['Context'];
autocompleteInfo: AutocompleteInfo;
docLinks: DocLinksStart['links'];
docLinkVersion: string;
esHostService: EsHostService;
history: History;
http: HttpSetup;
notifications: NotificationsStart;
objectStorageClient: ObjectStorageClient;
settings: Settings;
storage: Storage;
theme$: Observable<CoreTheme>;
trackUiMetric: MetricsTracker;
}

const loadDependencies = ({
core,
usageCollection,
}: EmbeddableConsoleDependencies): ConsoleDependencies => {
const {
docLinks: { DOC_LINK_VERSION, links },
http,
i18n: { Context: I18nContext },
notifications,
theme: { theme$ },
} = core;
const trackUiMetric = createUsageTracker(usageCollection);
trackUiMetric.load('opened_remote_app');

const autocompleteInfo = getAutocompleteInfo();
const storage = createStorage({
engine: window.localStorage,
prefix: 'sense:',
});
setStorage(storage);
const history = createHistory({ storage });
const settings = createSettings({ storage });
const objectStorageClient = localStorageObjectClient.create(storage);
const api = createApi({ http });
const esHostService = createEsHostService({ api });

autocompleteInfo.mapping.setup(http, settings);
return {
I18nContext,
autocompleteInfo,
docLinks: links,
docLinkVersion: DOC_LINK_VERSION,
esHostService,
history,
http,
notifications,
objectStorageClient,
settings,
storage,
theme$,
trackUiMetric,
};
};

export const ConsoleWrapper = (props: EmbeddableConsoleDependencies): React.ReactElement => {
const dependencies = useMemo(() => loadDependencies(props), [props]);
useEffect(() => {
return () => {
dependencies.autocompleteInfo.clearSubscriptions();
};
}, [dependencies]);

const {
I18nContext,
autocompleteInfo,
docLinkVersion,
docLinks,
esHostService,
history,
http,
notifications,
objectStorageClient,
settings,
storage,
theme$,
trackUiMetric,
} = dependencies;
return (
<I18nContext>
<KibanaThemeProvider theme={{ theme$ }}>
<ServicesContextProvider
value={{
docLinkVersion,
docLinks,
services: {
esHostService,
storage,
history,
settings,
notifications,
trackUiMetric,
objectStorageClient,
http,
autocompleteInfo,
},
theme$,
}}
>
<RequestContextProvider>
<EditorContextProvider settings={settings.toJSON()}>
<Main hideWelcome />
</EditorContextProvider>
</RequestContextProvider>
</ServicesContextProvider>
</KibanaThemeProvider>
</I18nContext>
);
};
Loading

0 comments on commit 4f4857c

Please sign in to comment.