[SharedUX] Remove SCSS from kibana_react (#204497)

## Summary

Part of https://github.com/elastic/kibana-team/issues/1082

Removes all Sass files in `src/plugins/kibana_react` to replace with
styles declared with Emotion. This PR does not include any changes that
would be noticeable by end-users. It changes the internals to use a
different technology for styling components.

## References
1. https://emotion.sh/docs/globals
2. https://emotion.sh/docs/best-practices
3.
https://github.com/elastic/eui/discussions/6828#discussioncomment-10825360
This commit is contained in:
Tim Sullivan 2025-01-07 15:51:59 -07:00 committed by GitHub
parent 292111b0d6
commit 95094b21e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 62 additions and 690 deletions

View file

@ -7,5 +7,5 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export { KibanaPageTemplateSolutionNavAvatar, KibanaPageTemplateSolutionNav } from './solution_nav';
export { KibanaPageTemplateSolutionNavAvatar } from './solution_nav';
export * from './no_data_page';

View file

@ -1,274 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KibanaPageTemplateSolutionNav accepts EuiSideNavProps 1`] = `
<Fragment>
<EuiSideNavClass
aria-hidden={true}
className="kbnPageTemplateSolutionNav kbnPageTemplateSolutionNav--hidden"
data-test-subj="DTS"
heading={
<React.Fragment>
<strong>
Solution
</strong>
</React.Fragment>
}
isOpenOnMobile={false}
items={
Array [
Object {
"id": "1",
"items": Array [
Object {
"id": "1.1",
"items": undefined,
"name": "Ingest Node Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.2",
"items": undefined,
"name": "Logstash Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.3",
"items": undefined,
"name": "Beats Central Management",
"tabIndex": -1,
},
],
"name": "Ingest",
"tabIndex": -1,
},
Object {
"id": "2",
"items": Array [
Object {
"id": "2.1",
"items": undefined,
"name": "Index Management",
"tabIndex": -1,
},
Object {
"id": "2.2",
"items": undefined,
"name": "Index Lifecycle Policies",
"tabIndex": -1,
},
Object {
"id": "2.3",
"items": undefined,
"name": "Snapshot and Restore",
"tabIndex": -1,
},
],
"name": "Data",
"tabIndex": -1,
},
]
}
mobileTitle={
<React.Fragment>
<Memo(MemoizedFormattedMessage)
defaultMessage="{solutionName} Menu"
id="kibana-react.solutionNav.mobileTitleText"
values={
Object {
"solutionName": "Solution",
}
}
/>
</React.Fragment>
}
toggleOpenOnMobile={[Function]}
/>
<KibanaPageTemplateSolutionNavCollapseButton
isCollapsed={true}
/>
</Fragment>
`;
exports[`KibanaPageTemplateSolutionNav renders 1`] = `
<Fragment>
<EuiSideNavClass
aria-hidden={true}
className="kbnPageTemplateSolutionNav kbnPageTemplateSolutionNav--hidden"
heading={
<React.Fragment>
<strong>
Solution
</strong>
</React.Fragment>
}
isOpenOnMobile={false}
items={
Array [
Object {
"id": "1",
"items": Array [
Object {
"id": "1.1",
"items": undefined,
"name": "Ingest Node Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.2",
"items": undefined,
"name": "Logstash Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.3",
"items": undefined,
"name": "Beats Central Management",
"tabIndex": -1,
},
],
"name": "Ingest",
"tabIndex": -1,
},
Object {
"id": "2",
"items": Array [
Object {
"id": "2.1",
"items": undefined,
"name": "Index Management",
"tabIndex": -1,
},
Object {
"id": "2.2",
"items": undefined,
"name": "Index Lifecycle Policies",
"tabIndex": -1,
},
Object {
"id": "2.3",
"items": undefined,
"name": "Snapshot and Restore",
"tabIndex": -1,
},
],
"name": "Data",
"tabIndex": -1,
},
]
}
mobileTitle={
<React.Fragment>
<Memo(MemoizedFormattedMessage)
defaultMessage="{solutionName} Menu"
id="kibana-react.solutionNav.mobileTitleText"
values={
Object {
"solutionName": "Solution",
}
}
/>
</React.Fragment>
}
toggleOpenOnMobile={[Function]}
/>
<KibanaPageTemplateSolutionNavCollapseButton
isCollapsed={true}
/>
</Fragment>
`;
exports[`KibanaPageTemplateSolutionNav renders with icon 1`] = `
<Fragment>
<EuiSideNavClass
aria-hidden={true}
className="kbnPageTemplateSolutionNav kbnPageTemplateSolutionNav--hidden"
heading={
<React.Fragment>
<KibanaPageTemplateSolutionNavAvatar
iconType="logoElastic"
name="Solution"
/>
<strong>
Solution
</strong>
</React.Fragment>
}
isOpenOnMobile={false}
items={
Array [
Object {
"id": "1",
"items": Array [
Object {
"id": "1.1",
"items": undefined,
"name": "Ingest Node Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.2",
"items": undefined,
"name": "Logstash Pipelines",
"tabIndex": -1,
},
Object {
"id": "1.3",
"items": undefined,
"name": "Beats Central Management",
"tabIndex": -1,
},
],
"name": "Ingest",
"tabIndex": -1,
},
Object {
"id": "2",
"items": Array [
Object {
"id": "2.1",
"items": undefined,
"name": "Index Management",
"tabIndex": -1,
},
Object {
"id": "2.2",
"items": undefined,
"name": "Index Lifecycle Policies",
"tabIndex": -1,
},
Object {
"id": "2.3",
"items": undefined,
"name": "Snapshot and Restore",
"tabIndex": -1,
},
],
"name": "Data",
"tabIndex": -1,
},
]
}
mobileTitle={
<React.Fragment>
<KibanaPageTemplateSolutionNavAvatar
iconType="logoElastic"
name="Solution"
/>
<Memo(MemoizedFormattedMessage)
defaultMessage="{solutionName} Menu"
id="kibana-react.solutionNav.mobileTitleText"
values={
Object {
"solutionName": "Solution",
}
}
/>
</React.Fragment>
}
toggleOpenOnMobile={[Function]}
/>
<KibanaPageTemplateSolutionNavCollapseButton
isCollapsed={true}
/>
</Fragment>
`;

View file

@ -2,8 +2,24 @@
exports[`KibanaPageTemplateSolutionNavAvatar renders 1`] = `
<EuiAvatar
className="kbnPageTemplateSolutionNavAvatar"
color="plain"
css={
Array [
Object {
"map": undefined,
"name": "ym1nj7",
"next": undefined,
"styles": "
box-shadow:
0 .7px 1.4px rgba(0,0,0,0.07),
0 1.9px 4px rgba(0,0,0,0.05),
0 4.5px 10px rgba(0,0,0,0.05);
",
"toString": [Function],
},
false,
]
}
iconType="logoElastic"
name="Solution"
/>

View file

@ -7,9 +7,5 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export type { KibanaPageTemplateSolutionNavProps } from './solution_nav';
export { KibanaPageTemplateSolutionNav } from './solution_nav';
export type { KibanaPageTemplateSolutionNavAvatarProps } from './solution_nav_avatar';
export { KibanaPageTemplateSolutionNavAvatar } from './solution_nav_avatar';
export type { KibanaPageTemplateSolutionNavCollapseButtonProps } from './solution_nav_collapse_button';
export { KibanaPageTemplateSolutionNavCollapseButton } from './solution_nav_collapse_button';

View file

@ -1,26 +0,0 @@
// Put the page background color in the flyout version too
.kbnPageTemplateSolutionNav__flyout {
background-color: $euiPageBackgroundColor;
}
.kbnPageTemplateSolutionNav {
@include euiYScroll;
@include euiBreakpoint('m' ,'l', 'xl') {
width: 248px;
padding: $euiSizeL;
}
.kbnPageTemplateSolutionNavAvatar {
margin-right: $euiSize;
}
}
.kbnPageTemplateSolutionNav--hidden {
pointer-events: none;
opacity: 0;
@include euiCanAnimate {
transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance;
}
}

View file

@ -1,72 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import { shallow } from 'enzyme';
import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav';
const items: KibanaPageTemplateSolutionNavProps['items'] = [
{
name: 'Ingest',
id: '1',
items: [
{
name: 'Ingest Node Pipelines',
id: '1.1',
},
{
name: 'Logstash Pipelines',
id: '1.2',
},
{
name: 'Beats Central Management',
id: '1.3',
},
],
},
{
name: 'Data',
id: '2',
items: [
{
name: 'Index Management',
id: '2.1',
},
{
name: 'Index Lifecycle Policies',
id: '2.2',
},
{
name: 'Snapshot and Restore',
id: '2.3',
},
],
},
];
describe('KibanaPageTemplateSolutionNav', () => {
test('renders', () => {
const component = shallow(<KibanaPageTemplateSolutionNav name="Solution" items={items} />);
expect(component).toMatchSnapshot();
});
test('renders with icon', () => {
const component = shallow(
<KibanaPageTemplateSolutionNav name="Solution" icon="logoElastic" items={items} />
);
expect(component).toMatchSnapshot();
});
test('accepts EuiSideNavProps', () => {
const component = shallow(
<KibanaPageTemplateSolutionNav name="Solution" data-test-subj="DTS" items={items} />
);
expect(component).toMatchSnapshot();
});
});

View file

@ -1,162 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import './solution_nav.scss';
import React, { FunctionComponent, useState, Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiFlyout,
EuiSideNav,
EuiSideNavItemType,
EuiSideNavProps,
useIsWithinBreakpoints,
} from '@elastic/eui';
import classNames from 'classnames';
import {
KibanaPageTemplateSolutionNavAvatar,
KibanaPageTemplateSolutionNavAvatarProps,
} from './solution_nav_avatar';
import { KibanaPageTemplateSolutionNavCollapseButton } from './solution_nav_collapse_button';
export type KibanaPageTemplateSolutionNavProps = EuiSideNavProps<{}> & {
/**
* Name of the solution, i.e. "Observability"
*/
name: KibanaPageTemplateSolutionNavAvatarProps['name'];
/**
* Solution logo, i.e. "logoObservability"
*/
icon?: KibanaPageTemplateSolutionNavAvatarProps['iconType'];
/**
* Control the collapsed state
*/
isOpenOnDesktop?: boolean;
onCollapse?: () => void;
};
const setTabIndex = (items: Array<EuiSideNavItemType<{}>>, isHidden: boolean) => {
return items.map((item) => {
// @ts-ignore-next-line Can be removed on close of https://github.com/elastic/eui/issues/4925
item.tabIndex = isHidden ? -1 : undefined;
item.items = item.items && setTabIndex(item.items, isHidden);
return item;
});
};
/**
* A wrapper around EuiSideNav but also creates the appropriate title with optional solution logo
*/
export const KibanaPageTemplateSolutionNav: FunctionComponent<
KibanaPageTemplateSolutionNavProps
> = ({ name, icon, items, isOpenOnDesktop = false, onCollapse, ...rest }) => {
// The EuiShowFor and EuiHideFor components are not in sync with the euiBreakpoint() function :(
const isSmallerBreakpoint = useIsWithinBreakpoints(['xs', 's']);
const isMediumBreakpoint = useIsWithinBreakpoints(['m']);
const isLargerBreakpoint = useIsWithinBreakpoints(['l', 'xl']);
// This is used for both the EuiSideNav and EuiFlyout toggling
const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false);
const toggleOpenOnMobile = () => {
setIsSideNavOpenOnMobile(!isSideNavOpenOnMobile);
};
const isHidden = isLargerBreakpoint && !isOpenOnDesktop;
/**
* Create the avatar
*/
let solutionAvatar;
if (icon) {
solutionAvatar = <KibanaPageTemplateSolutionNavAvatar iconType={icon} name={name} />;
}
/**
* Create the titles
*/
const titleText = (
<Fragment>
{solutionAvatar}
<strong>{name}</strong>
</Fragment>
);
const mobileTitleText = (
<FormattedMessage
id="kibana-react.solutionNav.mobileTitleText"
defaultMessage="{solutionName} Menu"
values={{ solutionName: name || 'Navigation' }}
/>
);
/**
* Create the side nav component
*/
let sideNav;
if (items) {
const sideNavClasses = classNames('kbnPageTemplateSolutionNav', {
'kbnPageTemplateSolutionNav--hidden': isHidden,
});
sideNav = (
<EuiSideNav
aria-hidden={isHidden}
className={sideNavClasses}
heading={titleText}
mobileTitle={
<Fragment>
{solutionAvatar}
{mobileTitleText}
</Fragment>
}
toggleOpenOnMobile={toggleOpenOnMobile}
isOpenOnMobile={isSideNavOpenOnMobile}
items={setTabIndex(items, isHidden)}
{...rest}
/>
);
}
return (
<Fragment>
{isSmallerBreakpoint && sideNav}
{isMediumBreakpoint && (
<Fragment>
{isSideNavOpenOnMobile && (
<EuiFlyout
ownFocus={false}
outsideClickCloses
onClose={() => setIsSideNavOpenOnMobile(false)}
side="left"
size={248}
closeButtonPosition="outside"
className="kbnPageTemplateSolutionNav__flyout"
>
{sideNav}
</EuiFlyout>
)}
<KibanaPageTemplateSolutionNavCollapseButton
isCollapsed={true}
onClick={toggleOpenOnMobile}
/>
</Fragment>
)}
{isLargerBreakpoint && (
<Fragment>
{sideNav}
<KibanaPageTemplateSolutionNavCollapseButton
isCollapsed={!isOpenOnDesktop}
onClick={onCollapse}
/>
</Fragment>
)}
</Fragment>
);
};

View file

@ -1,14 +0,0 @@
.kbnPageTemplateSolutionNavAvatar {
@include euiBottomShadowSmall;
&.kbnPageTemplateSolutionNavAvatar--xxl {
@include euiBottomShadowMedium;
@include size(100px);
line-height: 100px;
border-radius: 100px;
display: inline-block;
background: $euiColorEmptyShade url('../../assets/texture.svg') no-repeat;
background-size: cover, 125%;
text-align: center;
}
}

View file

@ -7,12 +7,16 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import './solution_nav_avatar.scss';
import { css } from '@emotion/react';
import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
import { DistributiveOmit, EuiAvatar, EuiAvatarProps } from '@elastic/eui';
import {
DistributiveOmit,
EuiAvatar,
EuiAvatarProps,
useEuiTheme,
useEuiShadow,
} from '@elastic/eui';
export type KibanaPageTemplateSolutionNavAvatarProps = DistributiveOmit<EuiAvatarProps, 'size'> & {
/**
@ -27,16 +31,31 @@ export type KibanaPageTemplateSolutionNavAvatarProps = DistributiveOmit<EuiAvata
export const KibanaPageTemplateSolutionNavAvatar: FunctionComponent<
KibanaPageTemplateSolutionNavAvatarProps
> = ({ className, size, ...rest }) => {
const { euiTheme } = useEuiTheme();
const pageTemplateSolutionNavAvatarStyles = {
base: css(useEuiShadow('s')),
xxl: css`
${useEuiShadow('m')};
width: 100px;
height: 100px;
line-height: 100px;
border-radius: 100px;
display: inline-block;
background: ${euiTheme.colors.backgroundBasePlain} url('../../assets/texture.svg') no-repeat;
background-size: cover, 125%;
text-align: center;
`,
};
return (
// @ts-ignore Complains about ExclusiveUnion between `iconSize` and `iconType`, but works fine
// @ts-expect-error Complains about ExclusiveUnion between `iconSize` and `iconType`, but works fine
<EuiAvatar
className={classNames(
'kbnPageTemplateSolutionNavAvatar',
{
[`kbnPageTemplateSolutionNavAvatar--${size}`]: size,
},
className
)}
className={className}
css={[
pageTemplateSolutionNavAvatarStyles.base,
size === 'xxl' && pageTemplateSolutionNavAvatarStyles.xxl,
]}
color="plain"
size={size === 'xxl' ? 'xl' : size}
iconSize={size}

View file

@ -1,47 +0,0 @@
.kbnPageTemplateSolutionNavCollapseButton {
position: absolute;
opacity: 0;
left: 248px - $euiSize;
top: $euiSizeL;
z-index: 2;
@include euiCanAnimate {
transition: opacity $euiAnimSpeedFast, left $euiAnimSpeedFast, background $euiAnimSpeedFast;
}
&:hover,
&:focus {
transition-delay: 0s !important;
}
.kbnPageTemplate__pageSideBar:hover &,
&:hover,
&:focus {
opacity: 1;
left: 248px - $euiSizeL;
}
.kbnPageTemplate__pageSideBar:hover & {
transition-delay: $euiAnimSpeedSlow * 2;
}
&:not(&-isCollapsed) {
background-color: $euiColorEmptyShade !important; // Override all states
}
}
// Make the button take up the entire area of the collapsed navigation
.kbnPageTemplateSolutionNavCollapseButton-isCollapsed {
opacity: 1 !important;
transition-delay: 0s !important;
left: 0 !important;
right: 0;
top: 0;
bottom: 0;
height: 100%;
width: 100%;
border-radius: 0;
// Keep the icon at the top instead of it getting shifted to the center of the page
padding-top: $euiSizeL + $euiSizeS;
align-items: flex-start;
}

View file

@ -1,59 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import './solution_nav_collapse_button.scss';
import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
import { EuiButtonIcon, EuiButtonIconPropsForButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
export type KibanaPageTemplateSolutionNavCollapseButtonProps =
Partial<EuiButtonIconPropsForButton> & {
/**
* Boolean state of current collapsed status
*/
isCollapsed: boolean;
};
/**
* Creates the styled icon button for showing/hiding solution nav
*/
export const KibanaPageTemplateSolutionNavCollapseButton: FunctionComponent<
KibanaPageTemplateSolutionNavCollapseButtonProps
> = ({ className, isCollapsed, ...rest }) => {
const classes = classNames(
'kbnPageTemplateSolutionNavCollapseButton',
{
'kbnPageTemplateSolutionNavCollapseButton-isCollapsed': isCollapsed,
},
className
);
const collapseLabel = i18n.translate('kibana-react.solutionNav.collapsibleLabel', {
defaultMessage: 'Collapse side navigation',
});
const openLabel = i18n.translate('kibana-react.solutionNav.openLabel', {
defaultMessage: 'Open side navigation',
});
return (
<EuiButtonIcon
className={classes}
size="s"
color="text"
iconType={isCollapsed ? 'menuRight' : 'menuLeft'}
aria-label={isCollapsed ? openLabel : collapseLabel}
title={isCollapsed ? openLabel : collapseLabel}
{...rest}
/>
);
};

View file

@ -1,5 +0,0 @@
.urlTemplateEditor__container {
.monaco-editor .lines-content.monaco-editor-background {
margin: 0 $euiSizeS;
}
}

View file

@ -7,12 +7,12 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { css } from '@emotion/react';
import * as React from 'react';
import { useEuiTheme } from '@elastic/eui';
import { monaco } from '@kbn/monaco';
import { CodeEditor, HandlebarsLang, type CodeEditorProps } from '@kbn/code-editor';
import './styles.scss';
export interface UrlTemplateEditorVariable {
label: string;
title?: string;
@ -125,8 +125,15 @@ export const UrlTemplateEditor: React.FC<UrlTemplateEditorProps> = ({
};
}, [variables]);
const { euiTheme } = useEuiTheme();
const editorStyle = css({
'.monaco-editor .lines-content.monaco-editor-background': {
margin: `0 ${euiTheme.size.s}`,
},
});
return (
<div className={'urlTemplateEditor__container'} onKeyDown={handleKeyDown}>
<div data-test-subj="url-template-editor-container" css={editorStyle} onKeyDown={handleKeyDown}>
<Editor
languageId={HandlebarsLang}
height={height}

View file

@ -115,7 +115,9 @@ export function DashboardDrilldownsManageProvider({ getService }: FtrProviderCon
}
async fillInURLTemplate(destinationURLTemplate: string) {
const monaco = await find.byCssSelector('.urlTemplateEditor__container .monaco-editor');
const monaco = await find.byCssSelector(
'[data-test-subj="url-template-editor-container"] .monaco-editor'
);
await monaco.clickMouseButton();
await this.eraseInput(300);
await browser.pressKeys(destinationURLTemplate);

View file

@ -5581,9 +5581,6 @@
"kibana-react.pageFooter.changeDefaultRouteSuccessToast": "Page de destination mise à jour",
"kibana-react.pageFooter.changeHomeRouteLink": "Afficher une page différente à la connexion",
"kibana-react.pageFooter.makeDefaultRouteLink": "Choisir comme page de destination",
"kibana-react.solutionNav.collapsibleLabel": "Réduire la navigation latérale",
"kibana-react.solutionNav.mobileTitleText": "Menu {solutionName}",
"kibana-react.solutionNav.openLabel": "Ouvrir la navigation latérale",
"kibanaOverview.addData.sampleDataButtonLabel": "Essayer lexemple de données",
"kibanaOverview.addData.sectionTitle": "Ingérer des données",
"kibanaOverview.apps.title": "Explorer les applications",

View file

@ -5575,9 +5575,6 @@
"kibana-react.pageFooter.changeDefaultRouteSuccessToast": "ランディングページが更新されました",
"kibana-react.pageFooter.changeHomeRouteLink": "ログイン時に別のページを表示",
"kibana-react.pageFooter.makeDefaultRouteLink": "これをランディングページにする",
"kibana-react.solutionNav.collapsibleLabel": "サイドナビゲーションを折りたたむ",
"kibana-react.solutionNav.mobileTitleText": "{solutionName}メニュー",
"kibana-react.solutionNav.openLabel": "サイドナビゲーションを開く",
"kibanaOverview.addData.sampleDataButtonLabel": "サンプルデータを試す",
"kibanaOverview.addData.sectionTitle": "データを取り込む",
"kibanaOverview.apps.title": "これらのアプリを検索",

View file

@ -5531,9 +5531,6 @@
"kibana-react.pageFooter.changeDefaultRouteSuccessToast": "登陆页面已更新",
"kibana-react.pageFooter.changeHomeRouteLink": "登录时显示不同页面",
"kibana-react.pageFooter.makeDefaultRouteLink": "将此设为我的登陆页面",
"kibana-react.solutionNav.collapsibleLabel": "折叠侧边导航",
"kibana-react.solutionNav.mobileTitleText": "{solutionName} 菜单",
"kibana-react.solutionNav.openLabel": "打开侧边导航",
"kibanaOverview.addData.sampleDataButtonLabel": "试用我们的样例数据",
"kibanaOverview.addData.sectionTitle": "采集您的数据",
"kibanaOverview.apps.title": "浏览这些应用",