[SharedUX] SCSS migration Home plugin (#214859)

## Summary
- rewriting styles in home plugin using `@emotion/react`
- updating functional a11y and unit tests
- adding a util function in core-app for fullScreenGraphicsMixin mixin
This commit is contained in:
Paulina Shakirova 2025-03-25 19:13:36 +01:00 committed by GitHub
parent 109d79d3f0
commit 27ca807175
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 298 additions and 263 deletions

View file

@ -9,7 +9,12 @@
// This file replaces scss core/public/_mixins.scss
import { css } from '@emotion/react';
import { css, keyframes } from '@emotion/react';
import { COLOR_MODES_STANDARD, UseEuiTheme, euiCanAnimate } from '@elastic/eui';
import bg_top_branded from './styles/core_app/images/bg_top_branded.svg';
import bg_top_branded_dark from './styles/core_app/images/bg_top_branded_dark.svg';
import bg_bottom_branded from './styles/core_app/images/bg_bottom_branded.svg';
import bg_bottom_branded_dark from './styles/core_app/images/bg_bottom_branded_dark.svg';
// The `--kbnAppHeadersOffset` CSS variable is automatically updated by
// styles/rendering/_base.scss, based on whether the Kibana chrome has a
@ -19,3 +24,58 @@ export const kibanaFullBodyHeightCss = (additionalOffset = 0) => css`
100vh - var(--kbnAppHeadersOffset, var(--euiFixedHeadersOffset, 0)) - ${additionalOffset}px
);
`;
export const fullScreenGraphicsMixinStyles = (euiZLevel: number, euiTheme: UseEuiTheme) => {
const lightOrDarkTheme = (lightSvg: any, darkSvg: any) => {
return euiTheme.colorMode === COLOR_MODES_STANDARD.light ? lightSvg : darkSvg;
};
const fullScreenGraphicsFadeIn = keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
`;
return css({
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: euiZLevel + 1000,
background: 'inherit',
backgroundColor: euiTheme.euiTheme.colors.backgroundBasePlain,
opacity: 0,
overflow: 'auto',
[euiCanAnimate]: {
animation: `${fullScreenGraphicsFadeIn} ${euiTheme.euiTheme.animation.extraSlow} ${euiTheme.euiTheme.animation.resistance} 0s forwards`,
},
'.kbnBody--hasHeaderBanner &': {
top: 'var(--kbnHeaderBannerHeight)',
},
'&::before': {
position: 'fixed',
top: 0,
left: 0,
zIndex: 1,
width: '400px',
height: '400px',
content: `url(${lightOrDarkTheme(bg_top_branded, bg_top_branded_dark)})`,
},
'&::after': {
position: 'fixed',
bottom: 0,
right: 0,
zIndex: 1,
width: '400px',
height: '400px',
content: `url(${lightOrDarkTheme(bg_bottom_branded, bg_bottom_branded_dark)})`,
},
[`@media (max-width: ${euiTheme.euiTheme.breakpoint.l}px)`]: {
'&::before, &::after': {
content: 'none',
},
},
});
};

View file

@ -309,4 +309,4 @@ export type { CoreSystem } from '@kbn/core-root-browser-internal';
export { __kbnBootstrap__ } from '@kbn/core-root-browser-internal';
export { kibanaFullBodyHeightCss } from './cssUtils';
export { kibanaFullBodyHeightCss, fullScreenGraphicsMixinStyles } from './cssUtils';

View file

@ -16,7 +16,6 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { SampleDataTabKibanaProvider } from '@kbn/home-sample-data-tab';
import { HomeApp } from './components/home_app';
import { getServices } from './kibana_services';
import './index.scss';
export const renderApp = async (
element: HTMLElement,

View file

@ -1,13 +0,0 @@
.homDataAdd__illustration {
display: block;
margin-block: 0 #{-($euiSizeXL + $euiSizeXS)};
margin-inline: auto;
@include euiBreakpoint('m', 'l', 'xl') {
margin-block-end: -$euiSizeXL;
}
@include euiBreakpoint('l', 'xl') {
inline-size: 80%;
}
}

View file

@ -1,12 +0,0 @@
// Prefix all styles with "hom" to avoid conflicts.
// Examples
// homChart
// homChart__legend
// homChart__legend--small
// homChart__legend-isLoading
@import 'welcome';
@import 'solutions_section';
@import 'add_data';
@import 'manage_data';
@import 'tutorial/tutorial';

View file

@ -1,5 +0,0 @@
.homDataManage__item {
@include euiBreakpoint('l', 'xl') {
max-width: calc(33.33% - #{$euiSizeM * 2});
}
}

View file

@ -1,29 +0,0 @@
.homSolutions__content {
padding-block-start: 0;
}
.homSolutions__item {
@include euiBreakpoint('l', 'xl') {
max-inline-size: calc(33.33% - #{$euiSizeM * 2});
}
}
.homSolutionPanel {
img {
background-color: $euiColorPrimary;
max-block-size: $euiSize * 10;
object-fit: cover;
}
&--enterpriseSearch img {
background-color: $euiColorWarning;
}
&--observability img {
background-color: $euiColorAccent;
}
&--securitySolution img {
background-color: $euiColorAccentSecondary;
}
}

View file

@ -1,28 +0,0 @@
.homWelcome {
@include kibanaFullScreenGraphics($euiZLevel6);
}
.homWelcome__header {
position: relative;
padding: $euiSizeXL;
z-index: 10;
}
.homWelcome__logo {
margin-bottom: $euiSizeXL;
@include kibanaCircleLogo;
@include euiBottomShadowMedium;
}
.homWelcome__footerAction {
margin-right: $euiSizeS;
}
.homWelcome__content {
position: relative;
margin: auto;
max-width: 512px;
padding-left: $euiSizeXL;
padding-right: $euiSizeXL;
z-index: 10;
}

View file

@ -2,9 +2,8 @@
exports[`AddData render 1`] = `
<_EuiPageSection
aria-labelledby="homDataAdd__title"
aria-labelledby="homeDataAdd__title"
bottomBorder={true}
className="homDataAdd"
paddingSize="xl"
>
<EuiFlexGroup
@ -15,7 +14,7 @@ exports[`AddData render 1`] = `
size="s"
>
<h2
id="homDataAdd__title"
id="homeDataAdd__title"
>
<MemoizedFormattedMessage
defaultMessage="Get started by adding integrations"

View file

@ -9,6 +9,7 @@
import { i18n } from '@kbn/i18n';
import React, { FC, MouseEvent } from 'react';
import { css } from '@emotion/react';
import {
EuiButton,
EuiButtonEmpty,
@ -18,6 +19,9 @@ import {
EuiSpacer,
EuiText,
EuiTitle,
UseEuiTheme,
mathWithUnits,
useEuiMinBreakpoint,
} from '@elastic/eui';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import { FormattedMessage } from '@kbn/i18n-react';
@ -37,19 +41,33 @@ interface Props {
export const AddData: FC<Props> = ({ addBasePath, application, isDarkMode, isCloudEnabled }) => {
const { trackUiMetric, guidedOnboardingService } = getServices();
const euiBreakpointM = useEuiMinBreakpoint('m');
const euiBreakpointL = useEuiMinBreakpoint('l');
const styles = ({ euiTheme }: UseEuiTheme) =>
css({
display: 'block',
marginBlock: `0 -${mathWithUnits([euiTheme.size.xl, euiTheme.size.xs], (x, y) => x + y)}`,
marginInline: 'auto',
[euiBreakpointM]: {
marginBlockEnd: euiTheme.size.xl,
},
[euiBreakpointL]: {
inlineSize: '80%',
},
});
const canAccessIntegrations = application.capabilities.navLinks.integrations;
if (canAccessIntegrations) {
return (
<KibanaPageTemplate.Section
bottomBorder
paddingSize="xl"
className="homDataAdd"
aria-labelledby="homDataAdd__title"
aria-labelledby="homeDataAdd__title"
>
<EuiFlexGroup alignItems="flexEnd">
<EuiFlexItem>
<EuiTitle size="s">
<h2 id="homDataAdd__title">
<h2 id="homeDataAdd__title">
<FormattedMessage
id="home.addData.sectionTitle"
defaultMessage="Get started by adding integrations"
@ -135,7 +153,7 @@ export const AddData: FC<Props> = ({ addBasePath, application, isDarkMode, isClo
alt={i18n.translate('home.addData.illustration.alt.text', {
defaultMessage: 'Illustration of Elastic data integrations',
})}
className="homDataAdd__illustration"
css={styles}
src={
addBasePath('/plugins/kibanaReact/assets/') +
(isDarkMode

View file

@ -2,10 +2,9 @@
exports[`ManageData hide dev tools and stack management links if unavailable 1`] = `
<_EuiPageSection
aria-labelledby="homDataManage__title"
aria-labelledby="homeDataManage__title"
bottomBorder={true}
className="homDataManage"
data-test-subj="homDataManage"
data-test-subj="homeDataManage"
paddingSize="xl"
>
<EuiFlexGroup
@ -18,7 +17,7 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
size="s"
>
<h2
id="homDataManage__title"
id="homeDataManage__title"
>
<MemoizedFormattedMessage
defaultMessage="Management"
@ -29,11 +28,9 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup
className="homDataManage__content"
>
<EuiFlexGroup>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="security"
>
<Synopsis
@ -46,7 +43,7 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="monitoring"
>
<Synopsis
@ -59,7 +56,7 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="snapshot_restore"
>
<Synopsis
@ -72,7 +69,7 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="index_lifecycle_management"
>
<Synopsis
@ -90,10 +87,9 @@ exports[`ManageData hide dev tools and stack management links if unavailable 1`]
exports[`ManageData render 1`] = `
<_EuiPageSection
aria-labelledby="homDataManage__title"
aria-labelledby="homeDataManage__title"
bottomBorder={true}
className="homDataManage"
data-test-subj="homDataManage"
data-test-subj="homeDataManage"
paddingSize="xl"
>
<EuiFlexGroup
@ -106,7 +102,7 @@ exports[`ManageData render 1`] = `
size="s"
>
<h2
id="homDataManage__title"
id="homeDataManage__title"
>
<MemoizedFormattedMessage
defaultMessage="Management"
@ -116,7 +112,6 @@ exports[`ManageData render 1`] = `
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__actions"
grow={false}
>
<EuiFlexGroup
@ -142,7 +137,6 @@ exports[`ManageData render 1`] = `
}
>
<EuiButtonEmpty
className="kbnOverviewPageHeader__actionButton"
data-test-subj="homeDevTools"
flush="both"
href=""
@ -173,7 +167,6 @@ exports[`ManageData render 1`] = `
}
>
<EuiButtonEmpty
className="kbnOverviewPageHeader__actionButton"
data-test-subj="homeManage"
flush="both"
href=""
@ -190,11 +183,9 @@ exports[`ManageData render 1`] = `
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup
className="homDataManage__content"
>
<EuiFlexGroup>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="security"
>
<Synopsis
@ -207,7 +198,7 @@ exports[`ManageData render 1`] = `
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="monitoring"
>
<Synopsis
@ -220,7 +211,7 @@ exports[`ManageData render 1`] = `
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="snapshot_restore"
>
<Synopsis
@ -233,7 +224,7 @@ exports[`ManageData render 1`] = `
/>
</EuiFlexItem>
<EuiFlexItem
className="homDataManage__item"
css={[Function]}
key="index_lifecycle_management"
>
<Synopsis

View file

@ -8,7 +8,15 @@
*/
import React, { FC, MouseEvent } from 'react';
import { EuiButtonEmpty, EuiFlexGroup, EuiSpacer, EuiTitle, EuiFlexItem } from '@elastic/eui';
import { css } from '@emotion/react';
import {
EuiButtonEmpty,
EuiFlexGroup,
EuiSpacer,
EuiTitle,
EuiFlexItem,
UseEuiTheme,
} from '@elastic/eui';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import { FormattedMessage } from '@kbn/i18n-react';
import { METRIC_TYPE } from '@kbn/analytics';
@ -27,6 +35,7 @@ interface Props {
export const ManageData: FC<Props> = ({ addBasePath, application, features }) => {
const { share, trackUiMetric } = getServices();
const consoleHref = share.url.locators.get('CONSOLE_APP_LOCATOR')?.useUrl({});
const managementHref = share.url.locators
.get('MANAGEMENT_APP_LOCATOR')
@ -40,21 +49,20 @@ export const ManageData: FC<Props> = ({ addBasePath, application, features }) =>
<KibanaPageTemplate.Section
bottomBorder
paddingSize="xl"
className="homDataManage"
aria-labelledby="homDataManage__title"
data-test-subj="homDataManage"
aria-labelledby="homeDataManage__title"
data-test-subj="homeDataManage"
>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={1}>
<EuiTitle size="s">
<h2 id="homDataManage__title">
<h2 id="homeDataManage__title">
<FormattedMessage id="home.manageData.sectionTitle" defaultMessage="Management" />
</h2>
</EuiTitle>
</EuiFlexItem>
{isDevToolsEnabled || isManagementEnabled ? (
<EuiFlexItem className="homDataManage__actions" grow={false}>
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" responsive={false} wrap>
{/* Check if both the Dev Tools UI and the Console UI are enabled. */}
{isDevToolsEnabled && consoleHref !== undefined ? (
@ -66,7 +74,6 @@ export const ManageData: FC<Props> = ({ addBasePath, application, features }) =>
>
<EuiButtonEmpty
data-test-subj="homeDevTools"
className="kbnOverviewPageHeader__actionButton"
flush="both"
iconType="wrench"
href={consoleHref}
@ -89,7 +96,6 @@ export const ManageData: FC<Props> = ({ addBasePath, application, features }) =>
>
<EuiButtonEmpty
data-test-subj="homeManage"
className="kbnOverviewPageHeader__actionButton"
flush="both"
iconType="gear"
href={managementHref}
@ -109,9 +115,18 @@ export const ManageData: FC<Props> = ({ addBasePath, application, features }) =>
<EuiSpacer />
<EuiFlexGroup className="homDataManage__content">
<EuiFlexGroup>
{features.map((feature) => (
<EuiFlexItem className="homDataManage__item" key={feature.id}>
<EuiFlexItem
css={({ euiTheme }: UseEuiTheme) =>
css({
[`@media (min-width: ${euiTheme.breakpoint.l}px)`]: {
maxWidth: `calc(33.33% - ${euiTheme.size.l})`,
},
})
}
key={feature.id}
>
<Synopsis
description={feature.description}
iconType={feature.icon}

View file

@ -14,12 +14,8 @@
*/
import React from 'react';
import {
// @ts-ignore
EuiCard,
EuiButton,
EuiButtonEmpty,
} from '@elastic/eui';
import { EuiCard, EuiButton, EuiButtonEmpty, UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { getServices } from '../../kibana_services';
@ -51,14 +47,10 @@ export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) {
}
footer={
<footer>
<EuiButton fill className="homWelcome__footerAction" onClick={onConfirm}>
<EuiButton fill css={footerAction} onClick={onConfirm}>
<FormattedMessage id="home.tryButtonLabel" defaultMessage="Add integrations" />
</EuiButton>
<EuiButtonEmpty
className="homWelcome__footerAction"
onClick={onDecline}
data-test-subj="skipWelcomeScreen"
>
<EuiButtonEmpty css={footerAction} onClick={onDecline} data-test-subj="skipWelcomeScreen">
<FormattedMessage id="home.exploreButtonLabel" defaultMessage="Explore on my own" />
</EuiButtonEmpty>
</footer>
@ -66,3 +58,8 @@ export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) {
/>
);
}
const footerAction = ({ euiTheme }: UseEuiTheme) => {
return css({
marginRight: euiTheme.size.s,
});
};

View file

@ -2,11 +2,11 @@
exports[`SolutionPanel renders the solution panel for the given solution 1`] = `
<EuiFlexItem
className="homSolutions__item"
data-test-subj="homSolutionPanel homSolutionPanel_kibana"
css={[Function]}
data-test-subj="homeSolutionPanel homeSolutionPanel_kibana"
>
<EuiCard
className="homSolutionPanel homSolutionPanel--kibana"
className="homeSolutionPanel homeSolutionPanel--kibana"
description="Explore and analyze your data"
href="kibana_landing_page"
icon={

View file

@ -2,19 +2,13 @@
exports[`SolutionsSection renders a single solution 1`] = `
<_EuiPageSection
aria-labelledby="homSolutions__title"
aria-labelledby="homeSolutions__title"
bottomBorder={true}
className="homSolutions"
contentProps={
Object {
"className": "homSolutions__content",
}
}
paddingSize="xl"
>
<EuiScreenReaderOnly>
<h2
id="homSolutions__title"
id="homeSolutions__title"
>
<MemoizedFormattedMessage
defaultMessage="Pick your solution"
@ -22,9 +16,7 @@ exports[`SolutionsSection renders a single solution 1`] = `
/>
</h2>
</EuiScreenReaderOnly>
<EuiFlexGroup
className="homSolutions__content"
>
<EuiFlexGroup>
<SolutionPanel
addBasePath={[Function]}
key="kibana"
@ -45,19 +37,13 @@ exports[`SolutionsSection renders a single solution 1`] = `
exports[`SolutionsSection renders multiple solutions 1`] = `
<_EuiPageSection
aria-labelledby="homSolutions__title"
aria-labelledby="homeSolutions__title"
bottomBorder={true}
className="homSolutions"
contentProps={
Object {
"className": "homSolutions__content",
}
}
paddingSize="xl"
>
<EuiScreenReaderOnly>
<h2
id="homSolutions__title"
id="homeSolutions__title"
>
<MemoizedFormattedMessage
defaultMessage="Pick your solution"
@ -65,9 +51,7 @@ exports[`SolutionsSection renders multiple solutions 1`] = `
/>
</h2>
</EuiScreenReaderOnly>
<EuiFlexGroup
className="homSolutions__content"
>
<EuiFlexGroup>
<SolutionPanel
addBasePath={[Function]}
key="kibana"

View file

@ -9,7 +9,8 @@
import { snakeCase } from 'lodash';
import React, { FC, MouseEvent } from 'react';
import { EuiCard, EuiFlexItem } from '@elastic/eui';
import { css } from '@emotion/react';
import { EuiCard, EuiFlexItem, UseEuiTheme, mathWithUnits } from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { KibanaPageTemplateSolutionNavAvatar } from '@kbn/kibana-react-plugin/public';
import { FeatureCatalogueSolution } from '../../..';
@ -21,19 +22,16 @@ interface Props {
solution: FeatureCatalogueSolution;
}
const getSolutionGraphicURL = (solutionId: string) =>
`/plugins/kibanaReact/assets/solutions_${solutionId}.svg`;
export const SolutionPanel: FC<Props> = ({ addBasePath, solution }) => {
const { trackUiMetric } = getServices();
const getSolutionGraphicURL = (solutionId: string) =>
`/plugins/kibanaReact/assets/solutions_${solutionId}.svg`;
return (
<EuiFlexItem
className="homSolutions__item"
data-test-subj={`homSolutionPanel homSolutionPanel_${solution.id}`}
>
<EuiFlexItem css={styles} data-test-subj={`homeSolutionPanel homeSolutionPanel_${solution.id}`}>
<EuiCard
className={`homSolutionPanel homSolutionPanel--${solution.id}`}
className={`homeSolutionPanel homeSolutionPanel--${solution.id}`}
description={solution.description}
href={addBasePath(solution.path)}
icon={
@ -54,3 +52,29 @@ export const SolutionPanel: FC<Props> = ({ addBasePath, solution }) => {
</EuiFlexItem>
);
};
const styles = ({ euiTheme }: UseEuiTheme) =>
css({
[`@media (min-width: ${euiTheme.breakpoint.m}px)`]: {
maxInlineSize: `calc(33.33% - ${euiTheme.size.m} * 10)`,
},
'.homeSolutionPanel': {
img: {
backgroundColor: euiTheme.colors.primary,
maxBlockSize: mathWithUnits(euiTheme.size.m, (x) => x * 10),
objectFit: 'cover',
},
'&--enterpriseSearch img': {
backgroundColor: euiTheme.colors.warning,
},
'&--observability img': {
backgroundColor: euiTheme.colors.accent,
},
'&--securitySolution img': {
backgroundColor: euiTheme.colors.accentSecondary,
},
},
});

View file

@ -32,12 +32,10 @@ export const SolutionsSection: FC<Props> = ({ addBasePath, solutions }) => {
<KibanaPageTemplate.Section
bottomBorder
paddingSize="xl"
aria-labelledby="homSolutions__title"
className="homSolutions"
contentProps={{ className: 'homSolutions__content' }}
aria-labelledby="homeSolutions__title"
>
<EuiScreenReaderOnly>
<h2 id="homSolutions__title">
<h2 id="homeSolutions__title">
<FormattedMessage
id="home.solutionsSection.sectionTitle"
defaultMessage="Pick your solution"
@ -45,7 +43,7 @@ export const SolutionsSection: FC<Props> = ({ addBasePath, solutions }) => {
</h2>
</EuiScreenReaderOnly>
<EuiFlexGroup className="homSolutions__content">
<EuiFlexGroup>
{solutions.map((solution) => (
<SolutionPanel addBasePath={addBasePath} key={solution.id} solution={solution} />
))}

View file

@ -1,4 +0,0 @@
.homTutorial__instruction {
flex-shrink: 0;
}

View file

@ -10,13 +10,16 @@
import React from 'react';
import { render } from '@testing-library/react';
import { I18nProvider } from '@kbn/i18n-react';
import { EuiThemeProvider } from '@elastic/eui';
import './welcome.test.mocks';
import { Welcome } from './welcome';
test('should render a Welcome screen', () => {
const { getByText } = render(
<I18nProvider>
<Welcome urlBasePath="/" onSkip={() => {}} />
<EuiThemeProvider>
<Welcome urlBasePath="" onSkip={() => {}} />
</EuiThemeProvider>
</I18nProvider>
);
expect(getByText('Welcome to Elastic')).toBeInTheDocument();

View file

@ -13,91 +13,131 @@
* in Elasticsearch.
*/
import React from 'react';
import { EuiTitle, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiPortal } from '@elastic/eui';
import React, { useEffect } from 'react';
import {
EuiTitle,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPortal,
UseEuiTheme,
useEuiShadow,
mathWithUnits,
useEuiTheme,
} from '@elastic/eui';
import { METRIC_TYPE } from '@kbn/analytics';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { fullScreenGraphicsMixinStyles } from '@kbn/core/public';
import { getServices } from '../kibana_services';
import { SampleDataCard } from './sample_data';
interface Props {
interface WelcomeProps {
urlBasePath: string;
onSkip: () => void;
}
/**
* Shows a full-screen welcome page that gives helpful quick links to beginners.
*/
export class Welcome extends React.Component<Props> {
private services = getServices();
export const Welcome: React.FC<WelcomeProps> = ({ urlBasePath, onSkip }: WelcomeProps) => {
const services = getServices();
const euiShadowM = useEuiShadow('m');
const theme = useEuiTheme();
private hideOnEsc = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
this.props.onSkip();
}
const redirectToAddData = () => {
services.application.navigateToApp('integrations', { path: '/browse' });
};
private redirectToAddData() {
this.services.application.navigateToApp('integrations', { path: '/browse' });
}
private onSampleDataDecline = () => {
this.services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataDecline');
this.props.onSkip();
const onSampleDataDecline = () => {
services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataDecline');
onSkip();
};
private onSampleDataConfirm = () => {
this.services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataConfirm');
this.redirectToAddData();
const onSampleDataConfirm = () => {
services.trackUiMetric(METRIC_TYPE.CLICK, 'sampleDataConfirm');
redirectToAddData();
};
componentDidMount() {
const { welcomeService } = this.services;
this.services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount');
document.addEventListener('keydown', this.hideOnEsc);
useEffect(() => {
const hideOnEsc = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onSkip();
}
};
const { welcomeService } = services;
services.trackUiMetric(METRIC_TYPE.LOADED, 'welcomeScreenMount');
document.addEventListener('keydown', hideOnEsc);
welcomeService.onRendered();
}
componentWillUnmount() {
document.removeEventListener('keydown', this.hideOnEsc);
}
return () => {
document.removeEventListener('keydown', hideOnEsc);
};
}, [onSkip, services]);
render() {
const { urlBasePath } = this.props;
const { welcomeService } = this.services;
return (
<EuiPortal>
<div className="homWelcome" data-test-subj="homeWelcomeInterstitial">
<header className="homWelcome__header">
<div className="homWelcome__content eui-textCenter">
<EuiSpacer size="xl" />
<span className="homWelcome__logo">
<EuiIcon type="logoElastic" size="xxl" />
</span>
<EuiTitle size="l" className="homWelcome__title">
<h1>
<FormattedMessage id="home.welcomeTitle" defaultMessage="Welcome to Elastic" />
</h1>
</EuiTitle>
<EuiSpacer size="m" />
</div>
</header>
<div className="homWelcome__content homWelcome-body">
<EuiFlexGroup gutterSize="l">
<EuiFlexItem>
<SampleDataCard
urlBasePath={urlBasePath}
onConfirm={this.onSampleDataConfirm}
onDecline={this.onSampleDataDecline}
/>
<EuiSpacer size="s" />
{welcomeService.renderTelemetryNotice()}
</EuiFlexItem>
</EuiFlexGroup>
const { welcomeService } = services;
return (
<EuiPortal>
<div
data-test-subj="homeWelcomeInterstitial"
css={[
styles,
fullScreenGraphicsMixinStyles(Number(theme.euiTheme.levels.navigation), theme),
]}
>
<header className="homeWelcome__header">
<div className="homeWelcome__content eui-textCenter">
<EuiSpacer size="xl" />
<span
className="homeWelcome__logo"
css={css`
${euiShadowM}
`}
>
<EuiIcon type="logoElastic" size="xxl" />
</span>
<EuiTitle size="l">
<h1>
<FormattedMessage id="home.welcomeTitle" defaultMessage="Welcome to Elastic" />
</h1>
</EuiTitle>
<EuiSpacer size="m" />
</div>
</header>
<div className="homeWelcome__content">
<EuiFlexGroup gutterSize="l">
<EuiFlexItem>
<SampleDataCard
urlBasePath={urlBasePath}
onConfirm={onSampleDataConfirm}
onDecline={onSampleDataDecline}
/>
<EuiSpacer size="s" />
{welcomeService.renderTelemetryNotice()}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</EuiPortal>
);
}
}
</div>
</EuiPortal>
);
};
const styles = ({ euiTheme }: UseEuiTheme) =>
css({
'.homeWelcome__header': {
padding: euiTheme.size.xl,
zIndex: 10,
},
'.homeWelcome__logo': {
marginBottom: euiTheme.size.xl,
display: 'inline-block',
},
'.homeWelcome__content': {
margin: 'auto',
maxWidth: mathWithUnits(euiTheme.size.xxxxl, (x) => x * 8),
paddingLeft: euiTheme.size.xl,
paddingRight: euiTheme.size.xl,
zIndex: 10,
},
});

View file

@ -1 +0,0 @@
@import 'components/index';

View file

@ -4,7 +4,7 @@
"outDir": "target/types",
"isolatedModules": true
},
"include": ["common/**/*", "public/**/*", "server/**/*", ".storybook/**/*"],
"include": ["common/**/*", "public/**/*", "server/**/*", ".storybook/**/*", "../../../../../typings/emotion.d.ts"],
"kbn_references": [
"@kbn/core",
"@kbn/data-views-plugin",

View file

@ -30,7 +30,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(event.properties.target).to.be.an('array');
const targets = event.properties.target as string[];
expect(targets.includes('DIV')).to.be(true);
expect(targets.includes('class=homWelcome')).to.be(true);
expect(targets.includes('data-test-subj=homeWelcomeInterstitial')).to.be(true);
expect(targets.includes('BUTTON')).to.be(true);
expect(targets.includes('data-test-subj=skipWelcomeScreen')).to.be(true);

View file

@ -77,11 +77,11 @@ export class HomePageObject extends FtrService {
}
async getVisibileSolutions() {
const solutionPanels = await this.testSubjects.findAll('~homSolutionPanel', 2000);
const solutionPanels = await this.testSubjects.findAll('~homeSolutionPanel', 2000);
const panelAttributes = await Promise.all(
solutionPanels.map((panel) => panel.getAttribute('data-test-subj'))
);
return panelAttributes.map((attributeValue) => attributeValue?.split('homSolutionPanel_')[1]);
return panelAttributes.map((attributeValue) => attributeValue?.split('homeSolutionPanel_')[1]);
}
async goToSampleDataPage() {

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('Kibana overview page meets a11y requirements ', async () => {
await testSubjects.click('homSolutionPanel homSolutionPanel_kibana');
await testSubjects.click('homeSolutionPanel homeSolutionPanel_kibana');
await a11y.testAppSnapshot();
});
@ -48,19 +48,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
it('Enterprise search overview page meets a11y requirements ', async () => {
await home.clickGoHome();
await testSubjects.click('homSolutionPanel homSolutionPanel_enterpriseSearch');
await testSubjects.click('homeSolutionPanel homeSolutionPanel_enterpriseSearch');
await a11y.testAppSnapshot();
});
it('Observability overview page meets a11y requirements ', async () => {
await home.clickGoHome();
await testSubjects.click('homSolutionPanel homSolutionPanel_observability');
await testSubjects.click('homeSolutionPanel homeSolutionPanel_observability');
await a11y.testAppSnapshot();
});
it('Security overview page meets a11y requirements ', async () => {
await home.clickGoHome();
await testSubjects.click('homSolutionPanel homSolutionPanel_securitySolution');
await testSubjects.click('homeSolutionPanel homeSolutionPanel_securitySolution');
await a11y.testAppSnapshot();
});

View file

@ -74,7 +74,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('shows the management section', async () => {
await testSubjects.existOrFail('homDataManage', { timeout: 2000 });
await testSubjects.existOrFail('homeDataManage', { timeout: 2000 });
});
it('shows the "Manage" action item', async () => {
@ -124,7 +124,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('does not show the management section', async () => {
await testSubjects.missingOrFail('homDataManage', { timeout: 2000 });
await testSubjects.missingOrFail('homeDataManage', { timeout: 2000 });
});
it('does not show the "Manage" action item', async () => {