Create @kbn/css-utils package (#223933)

## 🧵 Summary

This PR introduces a few structural and cleanup improvements:

1. Moves core Emotion CSS helpers (`useMemoizedStyles` renamed as
`useMemoCss`, `kbnFullBodyHeightCss`, `kbnFullScreenBgCss`) from
`core/public` to a new package: `@kbn/css-utils`.
2. Removes a significant portion of legacy SCSS from the core plugin. 
3. Replaces scss mixin with emotion `kbnFullScreenBgCss` across Kibana
(we have scss and emotion version, but emotion version wasn't widely
used yet).
4. As a result of (3), some plugin tests were migrated to React Testing
Library. This was necessary because Emotion-generated snapshots in
Enzyme were difficult to read and maintain when moving to emotion.

### Considerations
I initially tried to add the package to the [shared-deps
bundle](8e15517ddd),
but couldn’t get the SVG imports for `kbnFullScreenBgCss` to work
correctly in that setup.

As a workaround, I opted to import the helpers directly from their
source files.

An alternative approach could be to convert the used SVGs into React
components and use those within the shared package. Or explore something
like a static package and try to somehow wire in that in the webpack
internal compilers, but it doesn't seem to be worth the effort at the
moment.


### 💡 Motivation

- These utils don’t need to live in Core and are now decoupled to
improve performance and flexibility.
- Importing from `core/public` (even just for a small hook) was adding
noticeable overhead to test runs:
  - ~1–2s delay on first Jest execution
  - ~200ms added on subsequent runs
  - Occasional CI timeouts due to deep import graph

### 👥 Ownership

I assigned this package to sharedux team. Thank you!

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Marta Bondyra 2025-06-20 22:48:44 +02:00 committed by GitHub
parent f2d4278086
commit de3604c905
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
95 changed files with 793 additions and 606 deletions

1
.github/CODEOWNERS vendored
View file

@ -438,6 +438,7 @@ src/platform/packages/shared/kbn-config-schema @elastic/kibana-core
src/platform/packages/shared/kbn-content-management-utils @elastic/kibana-data-discovery
src/platform/packages/shared/kbn-crypto @elastic/kibana-security
src/platform/packages/shared/kbn-crypto-browser @elastic/kibana-core
src/platform/packages/shared/kbn-css-utils @elastic/appex-sharedux
src/platform/packages/shared/kbn-custom-icons @elastic/obs-ux-logs-team
src/platform/packages/shared/kbn-cypress-config @elastic/kibana-operations
src/platform/packages/shared/kbn-data-grid-in-table-search @elastic/kibana-data-discovery

View file

@ -439,6 +439,7 @@
"@kbn/cross-cluster-replication-plugin": "link:x-pack/platform/plugins/private/cross_cluster_replication",
"@kbn/crypto": "link:src/platform/packages/shared/kbn-crypto",
"@kbn/crypto-browser": "link:src/platform/packages/shared/kbn-crypto-browser",
"@kbn/css-utils": "link:src/platform/packages/shared/kbn-css-utils",
"@kbn/custom-branding-plugin": "link:x-pack/platform/plugins/private/custom_branding",
"@kbn/custom-icons": "link:src/platform/packages/shared/kbn-custom-icons",
"@kbn/custom-integrations": "link:x-pack/solutions/observability/packages/kbn-custom-integrations",

View file

@ -288,10 +288,6 @@ export function getWebpackConfig(
extensions: ['.js', '.ts', '.tsx', '.json'],
mainFields: ['browser', 'module', 'main'],
alias: {
core_app_image_assets: Path.resolve(
worker.repoRoot,
'src/core/public/styles/core_app/images'
),
vega: Path.resolve(worker.repoRoot, 'node_modules/vega/build-es5/vega.js'),
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',

View file

@ -1,119 +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".
*/
// This file replaces scss core/public/_mixins.scss
import { css, keyframes } from '@emotion/react';
import { COLOR_MODES_STANDARD, UseEuiTheme, euiCanAnimate, useEuiTheme } from '@elastic/eui';
import { useMemo } from 'react';
import { CSSInterpolation } from '@emotion/serialize';
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
// header banner, app menu, and is visible or hidden
export const kibanaFullBodyHeightCss = (additionalOffset = '0px') =>
css({
height: `calc(100vh - var(--kbnAppHeadersOffset, var(--euiFixedHeadersOffset, 0)) - ${additionalOffset})`,
});
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',
},
},
});
};
export type EmotionStyles = Record<
string,
CSSInterpolation | ((theme: UseEuiTheme) => CSSInterpolation)
>;
type StaticEmotionStyles = Record<string, CSSInterpolation>;
/**
* Custom hook to reduce boilerplate when working with Emotion styles that may depend on
* the EUI theme.
*
* Accepts a map of styles where each entry is either a static Emotion style (via `css`)
* or a function that returns styles based on the current `euiTheme`.
*
* It returns a memoized version of the style map with all values resolved to static
* Emotion styles, allowing components to use a clean and unified object for styling.
*
* This helps simplify component code by centralizing theme-aware style logic.
*
* Example usage:
* const componentStyles = {
* container: css({ overflow: hidden }),
* leftPane: ({ euiTheme }) => css({ paddingTop: euiTheme.size.m }),
* }
* const styles = useMemoizedStyles(componentStyles);
*/
export const useMemoizedStyles = (styleMap: EmotionStyles) => {
const euiThemeContext = useEuiTheme();
const outputStyles = useMemo(() => {
return Object.entries(styleMap).reduce<StaticEmotionStyles>((acc, [key, value]) => {
acc[key] = typeof value === 'function' ? value(euiThemeContext) : value;
return acc;
}, {});
}, [euiThemeContext, styleMap]);
return outputStyles;
};

View file

@ -310,10 +310,3 @@ export type { CoreSetup, CoreStart, StartServicesAccessor } from '@kbn/core-life
export type { CoreSystem } from '@kbn/core-root-browser-internal';
export { __kbnBootstrap__ } from '@kbn/core-root-browser-internal';
export {
kibanaFullBodyHeightCss,
fullScreenGraphicsMixinStyles,
useMemoizedStyles,
type EmotionStyles,
} from './css_utils';

View file

@ -4,6 +4,4 @@
@import '@elastic/eui-theme-borealis/src/theme_dark.scss';
@import './mixins';
$kbnThemeVersion: 'borealisdark';

View file

@ -4,6 +4,4 @@
@import '@elastic/eui-theme-borealis/src/theme_light.scss';
@import './mixins';
$kbnThemeVersion: 'borealislight';

View file

@ -5,6 +5,4 @@
@import '@elastic/eui/src/themes/amsterdam/colors_dark';
@import '@elastic/eui/src/themes/amsterdam/globals';
@import './mixins';
$kbnThemeVersion: 'v8dark';

View file

@ -5,6 +5,4 @@
@import '@elastic/eui/src/themes/amsterdam/colors_light';
@import '@elastic/eui/src/themes/amsterdam/globals';
@import './mixins';
$kbnThemeVersion: 'v8light';

View file

@ -1,69 +0,0 @@
@mixin flexParent($grow: 1, $shrink: 1, $basis: auto, $direction: column) {
flex: $grow $shrink $basis;
display: flex;
flex-direction: $direction;
> * {
flex-shrink: 0;
}
}
@mixin kibanaCircleLogo() {
display: inline-block;
}
@mixin kibanaFullScreenGraphics($euiZLevel: $euiZLevel9) {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: $euiZLevel + 1000;
background: inherit;
background-color: $euiPageBackgroundColor;
opacity: 0;
overflow: auto;
animation: kibanaFullScreenGraphics_FadeIn $euiAnimSpeedExtraSlow $euiAnimSlightResistance 0s forwards;
@at-root {
.kbnBody--hasHeaderBanner & {
top: var(--kbnHeaderBannerHeight);
}
}
&::before {
position: fixed;
top: 0;
left: 0;
z-index: 1;
width: 400px;
height: 400px;
content: url(lightOrDarkTheme('~core_app_image_assets/bg_top_branded.svg', '~core_app_image_assets/bg_top_branded_dark.svg'));
}
&::after {
position: fixed;
bottom: 0;
right: 0;
z-index: 1;
width: 400px;
height: 400px;
content: url(lightOrDarkTheme('~core_app_image_assets/bg_bottom_branded.svg', '~core_app_image_assets/bg_bottom_branded_dark.svg'));
}
@keyframes kibanaFullScreenGraphics_FadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@media only screen and (max-width: map-get($euiBreakpoints, 'l')) {
&::before, &::after {
content: none;
}
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 82 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 104 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 102 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 115 KiB

View file

@ -118,7 +118,7 @@ export class ImportResolver {
}
// these are special webpack-aliases only used in storybooks, ignore them
if (req === 'core_styles' || req === 'core_app_image_assets') {
if (req === 'core_styles') {
return true;
}

View file

@ -95,12 +95,6 @@ describe('#resolve()', () => {
}
`);
expect(resolver.resolve('core_app_image_assets', FIXTURES_DIR)).toMatchInlineSnapshot(`
Object {
"type": "ignore",
}
`);
expect(resolver.resolve('@elastic/eui/src/components/foo', FIXTURES_DIR))
.toMatchInlineSnapshot(`
Object {

View file

@ -0,0 +1,18 @@
# @kbn/css-utils
Shared Emotion-based CSS utilities for use across Kibana.
This package includes reusable styling helpers designed to support styling with Emotion.
## ✨ Included Utilities
- `useMemoCss`: React hook to memoize Emotion styles.
- `kbnFullBodyHeightCss`: Utility for full-body-height layout styling.
- `kbnFullScreenBgCss`: Full-screen graphics styling helper, used in various login page.
## 📌 Usage
To prevent unnecessary chunk size bloat, always import directly from specific helper files:
```ts
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';

View file

@ -0,0 +1,14 @@
/*
* 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".
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../../../..',
roots: ['<rootDir>/src/platform/packages/shared/kbn-css-utils'],
};

View file

@ -0,0 +1,9 @@
{
"type": "shared-common",
"id": "@kbn/css-utils",
"owner": [
"@elastic/appex-sharedux",
],
"group": "platform",
"visibility": "shared"
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/css-utils",
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0",
"private": true,
"sideEffects": false
}

View file

@ -0,0 +1,20 @@
/*
* 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".
*/
// This file replaces scss core/public/_mixins.scss
import { css } from '@emotion/react';
// The `--kbnAppHeadersOffset` CSS variable is automatically updated by
// styles/rendering/_base.scss, based on whether the Kibana chrome has a
// header banner, app menu, and is visible or hidden
export const kbnFullBodyHeightCss = (additionalOffset = '0px') =>
css({
height: `calc(100vh - var(--kbnAppHeadersOffset, var(--euiFixedHeadersOffset, 0)) - ${additionalOffset})`,
});

View file

@ -0,0 +1,76 @@
/*
* 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".
*/
// This file replaces scss core/public/_mixins.scss
import { useMemo } from 'react';
import { css, keyframes } from '@emotion/react';
import { COLOR_MODES_STANDARD, UseEuiTheme, euiCanAnimate, useEuiTheme } from '@elastic/eui';
import bg_top_branded from './images/bg_top_branded.svg';
import bg_top_branded_dark from './images/bg_top_branded_dark.svg';
import bg_bottom_branded from './images/bg_bottom_branded.svg';
import bg_bottom_branded_dark from './images/bg_bottom_branded_dark.svg';
export const kbnFullScreenBgCss = ({ euiTheme, colorMode }: UseEuiTheme) => {
const lightOrDarkTheme = (lightSvg: string, darkSvg: any) => {
return 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: Number(euiTheme.levels.navigation) + 1000,
background: 'inherit',
backgroundColor: euiTheme.colors.backgroundBasePlain,
opacity: 0,
overflow: 'auto',
[euiCanAnimate]: {
animation: `${fullScreenGraphicsFadeIn} ${euiTheme.animation.extraSlow} ${euiTheme.animation.resistance} 0s forwards`,
},
'.kbnBody--hasHeaderBanner &': {
top: 'var(--kbnHeaderBannerHeight)',
},
'&::before, &::after': {
position: 'fixed',
zIndex: 1,
width: '400px',
height: '400px',
content: `url(${lightOrDarkTheme(bg_top_branded, bg_top_branded_dark)})`,
[`@media (max-width: ${euiTheme.breakpoint.l}px)`]: {
content: 'none',
},
},
'&::before': {
top: 0,
left: 0,
content: `url(${lightOrDarkTheme(bg_top_branded, bg_top_branded_dark)})`,
},
'&::after': {
bottom: 0,
right: 0,
content: `url(${lightOrDarkTheme(bg_bottom_branded, bg_bottom_branded_dark)})`,
},
});
};
export const useKbnFullScreenBgCss = () => {
const euiTheme = useEuiTheme();
const styles = useMemo(() => kbnFullScreenBgCss(euiTheme), [euiTheme]);
return styles;
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 84 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 103 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 116 KiB

View file

@ -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", 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 { useMemo } from 'react';
import { CSSInterpolation } from '@emotion/css';
import { UseEuiTheme, useEuiTheme } from '@elastic/eui';
export type EmotionStyles = Record<
string,
CSSInterpolation | ((theme: UseEuiTheme) => CSSInterpolation)
>;
type StaticEmotionStyles = Record<string, CSSInterpolation>;
/**
* Custom hook to reduce boilerplate when working with Emotion styles that may depend on
* the EUI theme.
*
* Accepts a map of styles where each entry is either a static Emotion style (via `css`)
* or a function that returns styles based on the current `euiTheme`.
*
* It returns a memoized version of the style map with all values resolved to static
* Emotion styles, allowing components to use a clean and unified object for styling.
*
* This helps simplify component code by centralizing theme-aware style logic.
*
* Example usage:
* const componentStyles = {
* container: css({ overflow: hidden }),
* leftPane: ({ euiTheme }) => css({ paddingTop: euiTheme.size.m }),
* }
* const styles = useMemoCss(componentStyles);
*/
export const useMemoCss = (styleMap: EmotionStyles) => {
const euiThemeContext = useEuiTheme();
const outputStyles = useMemo(() => {
return Object.entries(styleMap).reduce<StaticEmotionStyles>((acc, [key, value]) => {
acc[key] = typeof value === 'function' ? value(euiThemeContext) : value;
return acc;
}, {});
}, [euiThemeContext, styleMap]);
return outputStyles;
};

View file

@ -0,0 +1,20 @@
{
"extends": "../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react",
"@kbn/ambient-ui-types",
],
},
"include": [
"**/*.ts",
],
"kbn_references": [
],
"exclude": [
"target/**/*",
]
}

View file

@ -53,7 +53,6 @@ export default ({ config: storybookConfig }: { config: Configuration }) => {
extensions: ['.js', '.mjs', '.ts', '.tsx', '.json', '.mdx'],
mainFields: ['browser', 'main'],
alias: {
core_app_image_assets: resolve(REPO_ROOT, 'src/core/public/styles/core_app/images'),
core_styles: resolve(REPO_ROOT, 'src/core/public/index.scss'),
vega: resolve(REPO_ROOT, 'node_modules/vega/build-es5/vega.js'),
},

View file

@ -1,7 +1,3 @@
.interactiveSetup {
@include kibanaFullScreenGraphics;
}
.interactiveSetup__header {
position: relative;
z-index: 10;
@ -11,7 +7,7 @@
.interactiveSetup__logo {
margin-bottom: $euiSizeXL;
@include kibanaCircleLogo;
display: inline-block;
@include euiBottomShadowMedium;
}

View file

@ -14,6 +14,7 @@ import type { FunctionComponent } from 'react';
import React, { useState } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { useKbnFullScreenBgCss } from '@kbn/css-utils/public/full_screen_bg_css';
import { FormattedMessage } from '@kbn/i18n-react';
import { ClusterAddressForm } from './cluster_address_form';
@ -38,6 +39,8 @@ export const App: FunctionComponent<AppProps> = ({ onSuccess }) => {
[http]
);
const kbnFullScreenBgCss = useKbnFullScreenBgCss();
if (state.loading) {
return null;
}
@ -54,7 +57,7 @@ export const App: FunctionComponent<AppProps> = ({ onSuccess }) => {
}
return (
<div className="interactiveSetup">
<div css={kbnFullScreenBgCss}>
<header className="interactiveSetup__header eui-textCenter">
<EuiSpacer size="xxl" />
<span className="interactiveSetup__logo">

View file

@ -25,6 +25,7 @@
"@kbn/core-preboot-server",
"@kbn/security-hardening",
"@kbn/react-kibana-context-render",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -2,6 +2,16 @@
// LAYOUT
//
@mixin flexParent($grow: 1, $shrink: 1, $basis: auto, $direction: column) {
flex: $grow $shrink $basis;
display: flex;
flex-direction: $direction;
> * {
flex-shrink: 0;
}
}
.visEditorSidebar {
height: 100%;
padding-left: $euiSizeS;

View file

@ -20,7 +20,7 @@ import {
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { BehaviorSubject } from 'rxjs';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import classNames from 'classnames';
import { DEFAULT_CONTROL_GROW } from '../../../common';
@ -46,7 +46,7 @@ export const ControlClone = ({
);
const isTwoLine = labelPosition === 'twoLine';
const styles = useMemoizedStyles(controlCloneStyles);
const styles = useMemoCss(controlCloneStyles);
return (
<EuiFlexItem

View file

@ -15,7 +15,7 @@ import { Markdown } from '@kbn/shared-ux-markdown';
import { useErrorTextStyle } from '@kbn/react-hooks';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
interface ControlErrorProps {
error: Error | string;
@ -51,7 +51,7 @@ export const ControlError = ({ error }: ControlErrorProps) => {
const errorTextStyle = useErrorTextStyle();
const [isPopoverOpen, setPopoverOpen] = useState(false);
const errorMessage = error instanceof Error ? error.message : error || defaultMessage;
const styles = useMemoizedStyles(controlErrorStyles);
const styles = useMemoCss(controlErrorStyles);
const popoverButton = (
<EuiButtonEmpty

View file

@ -25,7 +25,7 @@ import {
apiPublishesViewMode,
useBatchedOptionalPublishingSubjects,
} from '@kbn/presentation-publishing';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import classNames from 'classnames';
import { FloatingActions } from './floating_actions';
import { DEFAULT_CONTROL_GROW, DEFAULT_CONTROL_WIDTH } from '../../../common';
@ -104,7 +104,7 @@ export const ControlPanel = <ApiType extends DefaultControlApi = DefaultControlA
const insertBefore = isOver && (index ?? -1) < (activeIndex ?? -1);
const insertAfter = isOver && (index ?? -1) > (activeIndex ?? -1);
const styles = useMemoizedStyles(controlPanelStyles);
const styles = useMemoCss(controlPanelStyles);
return (
<EuiFlexItem

View file

@ -17,7 +17,7 @@ import { Subscription, switchMap } from 'rxjs';
import { ViewMode, apiHasUniqueId } from '@kbn/presentation-publishing';
import { Action } from '@kbn/ui-actions-plugin/public';
import { AnyApiAction } from '@kbn/presentation-panel-plugin/public/panel_actions/types';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { uiActionsService } from '../../services/kibana_services';
import { CONTROL_HOVER_TRIGGER, controlHoverTrigger } from '../../actions/controls_hover_trigger';
@ -119,7 +119,7 @@ export const FloatingActions: FC<FloatingActionsProps> = ({
};
}, [api, viewMode, disabledActions]);
const styles = useMemoizedStyles(floatingActionsStyles);
const styles = useMemoCss(floatingActionsStyles);
return (
<div css={styles.wrapper}>

View file

@ -24,7 +24,7 @@ import {
} from '@elastic/eui';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { isCompressed } from '../../../../control_group/utils/is_compressed';
import { OptionsListSelection } from '../../../../../common/options_list/options_list_selections';
import { MIN_POPOVER_WIDTH } from '../../../constants';
@ -121,7 +121,7 @@ export const OptionsListControl = ({
);
const delimiter = useMemo(() => OptionsListStrings.control.getSeparator(field?.type), [field]);
const styles = useMemoizedStyles(optionListControlStyles);
const styles = useMemoCss(optionListControlStyles);
const { hasSelections, selectionDisplayNode, selectedOptionsCount } = useMemo(() => {
return {

View file

@ -24,7 +24,7 @@ import {
useStateFromPublishingSubject,
} from '@kbn/presentation-publishing';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { getCompatibleSearchTechniques } from '../../../../../common/options_list/suggestions_searching';
import { useOptionsListContext } from '../options_list_context_provider';
import { OptionsListPopoverSortingButton } from './options_list_popover_sorting_button';
@ -89,7 +89,7 @@ export const OptionsListPopoverActionBar = ({
[searchTechnique, compatibleSearchTechniques]
);
const styles = useMemoizedStyles(optionsListPopoverStyles);
const styles = useMemoCss(optionsListPopoverStyles);
return (
<div className="optionsList__actions" css={styles.actions}>

View file

@ -26,7 +26,7 @@ import {
} from '@kbn/presentation-publishing';
import { BehaviorSubject } from 'rxjs';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { useOptionsListContext } from '../options_list_context_provider';
import { OptionsListStrings } from '../options_list_strings';
@ -39,7 +39,7 @@ const optionsListPopoverInvalidSelectionsStyles = {
export const OptionsListPopoverInvalidSelections = () => {
const { componentApi } = useOptionsListContext();
const styles = useMemoizedStyles(optionsListPopoverInvalidSelectionsStyles);
const styles = useMemoCss(optionsListPopoverInvalidSelectionsStyles);
const [invalidSelections, fieldFormatter] = useBatchedPublishingSubjects(
componentApi.invalidSelections$,

View file

@ -14,7 +14,7 @@ import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/sele
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { OptionsListSuggestions } from '../../../../../common/options_list/types';
import { OptionsListSelection } from '../../../../../common/options_list/options_list_selections';
import { MAX_OPTIONS_LIST_REQUEST_SIZE } from '../constants';
@ -36,7 +36,7 @@ export const OptionsListPopoverSuggestions = ({
} = useOptionsListContext();
const { euiTheme } = useEuiTheme();
const styles = useMemoizedStyles(optionListPopoverSuggestionsStyles);
const styles = useMemoCss(optionListPopoverSuggestionsStyles);
const [
sort,

View file

@ -10,7 +10,7 @@
import React from 'react';
import { EuiText, UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
interface Props {
onClick: () => void;
@ -52,7 +52,7 @@ const timeSliderStyles = {
};
export function TimeSliderPopoverButton(props: Props) {
const styles = useMemoizedStyles(timeSliderStyles);
const styles = useMemoCss(timeSliderStyles);
return (
<button
className="eui-textTruncate"

View file

@ -40,6 +40,7 @@
"@kbn/std",
"@kbn/react-hooks",
"@kbn/esql-types",
"@kbn/css-utils",
],
"exclude": ["target/**/*"]
}

View file

@ -22,7 +22,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ViewMode } from '@kbn/presentation-publishing';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import type { DashboardAttributes } from '../../server/content_management';
import {
DASHBOARD_PANELS_UNSAVED_ID,
@ -77,7 +77,7 @@ const DashboardUnsavedItem = ({
onOpenClick: () => void;
onDiscardClick: () => void;
}) => {
const styles = useMemoizedStyles(unsavedItemStyles);
const styles = useMemoCss(unsavedItemStyles);
return (
<div css={styles.item}>
<EuiFlexGroup alignItems="center" gutterSize="none" css={styles.heading} responsive={false}>

View file

@ -14,7 +14,7 @@ import { GridLayout, GridPanelData, GridSectionData, type GridLayoutData } from
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import classNames from 'classnames';
import { default as React, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { DASHBOARD_GRID_COLUMN_COUNT } from '../../../common/content_management/constants';
import { GridData } from '../../../common/content_management/v2/types';
import { areLayoutsEqual } from '../../dashboard_api/are_layouts_equal';
@ -167,7 +167,7 @@ export const DashboardGrid = ({
[appFixedViewport, dashboardContainerRef, dashboardInternalApi.layout$]
);
const styles = useMemoizedStyles(dashboardGridStyles);
const styles = useMemoCss(dashboardGridStyles);
useEffect(() => {
/**

View file

@ -9,11 +9,11 @@
import { EuiLoadingChart, UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { EmbeddableRenderer } from '@kbn/embeddable-plugin/public';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import classNames from 'classnames';
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { DashboardPanelState } from '../../../common';
import { useDashboardApi } from '../../dashboard_api/use_dashboard_api';
import { useDashboardInternalApi } from '../../dashboard_api/use_dashboard_internal_api';
@ -106,7 +106,7 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
const dashboardContainerTopOffset = dashboardContainerRef?.current?.offsetTop || 0;
const globalNavTopOffset = appFixedViewport?.offsetTop || 0;
const styles = useMemoizedStyles(dashboardGridItemStyles);
const styles = useMemoCss(dashboardGridItemStyles);
const renderedEmbeddable = useMemo(() => {
const panelProps = {

View file

@ -17,7 +17,7 @@ import { ExitFullScreenButton } from '@kbn/shared-ux-button-exit-full-screen';
import { CONTROL_GROUP_TYPE } from '@kbn/controls-plugin/common';
import { ControlGroupApi } from '@kbn/controls-plugin/public';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { CONTROL_GROUP_EMBEDDABLE_ID } from '../../dashboard_api/control_group_manager';
import { useDashboardApi } from '../../dashboard_api/use_dashboard_api';
import { useDashboardInternalApi } from '../../dashboard_api/use_dashboard_internal_api';
@ -103,7 +103,7 @@ export const DashboardViewport = ({
}, [dashboard]);
*/
const styles = useMemoizedStyles(dashboardViewportStyles);
const styles = useMemoCss(dashboardViewportStyles);
return (
<div

View file

@ -24,7 +24,7 @@ import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import useMountedState from 'react-use/lib/useMountedState';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
import { coreServices } from '../../../services/kibana_services';
import { getDashboardCapabilities } from '../../../utils/get_dashboard_capabilities';
@ -43,7 +43,7 @@ export function DashboardEmptyScreen() {
const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode$);
const isEditMode = viewMode === 'edit';
const styles = useMemoizedStyles(emptyScreenStyles);
const styles = useMemoCss(emptyScreenStyles);
// TODO replace these SVGs with versions from EuiIllustration as soon as it becomes available.
const imageUrl = coreServices.http.basePath.prepend(

View file

@ -21,7 +21,8 @@ import {
UseEuiTheme,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { MountPoint, useMemoizedStyles } from '@kbn/core/public';
import { MountPoint } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { Query } from '@kbn/es-query';
import { FormattedMessage } from '@kbn/i18n-react';
import { getManagedContentBadge } from '@kbn/managed-content-badge';
@ -114,7 +115,7 @@ export function InternalDashboardTopNav({
return getDashboardTitle(title, viewMode, !lastSavedId);
}, [title, viewMode, lastSavedId]);
const styles = useMemoizedStyles(topNavStyles);
const styles = useMemoCss(topNavStyles);
/**
* focus on the top header when title or view mode is changed

View file

@ -85,7 +85,8 @@
"@kbn/esql-types",
"@kbn/saved-objects-tagging-plugin",
"@kbn/react-kibana-context-theme",
"@kbn/std"
"@kbn/std",
"@kbn/css-utils"
],
"exclude": ["target/**/*"]
}

View file

@ -29,7 +29,7 @@ import type { UseColumnsProps } from '@kbn/unified-data-table';
import { popularizeField, useColumns } from '@kbn/unified-data-table';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common';
import { kibanaFullBodyHeightCss } from '@kbn/core/public';
import { kbnFullBodyHeightCss } from '@kbn/css-utils/public/full_body_height_css';
import { ContextErrorMessage } from './components/context_error_message';
import { LoadingStatus } from './services/context_query_state';
import type { AppState, GlobalState } from './services/context_state';
@ -331,5 +331,5 @@ const styles = {
flexDirection: 'column',
height: '100%',
}),
docsPage: kibanaFullBodyHeightCss('54px'), // 54px is the action bar height
docsPage: kbnFullBodyHeightCss('54px'), // 54px is the action bar height
};

View file

@ -32,7 +32,7 @@ import { popularizeField, useColumns } from '@kbn/unified-data-table';
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
import { BehaviorSubject } from 'rxjs';
import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common';
import { kibanaFullBodyHeightCss } from '@kbn/core/public';
import { kbnFullBodyHeightCss } from '@kbn/css-utils/public/full_body_height_css';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useSavedSearchInitial } from '../../state_management/discover_state_provider';
import type { DiscoverStateContainer } from '../../state_management/discover_state';
@ -378,7 +378,7 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
background-color: ${pageBackgroundColor};
${useEuiBreakpoint(['m', 'l', 'xl'])} {
${kibanaFullBodyHeightCss(TABS_ENABLED ? '32px' : undefined)}
${kbnFullBodyHeightCss(TABS_ENABLED ? '32px' : undefined)}
}
`}
>

View file

@ -106,7 +106,8 @@
"@kbn/unified-tabs",
"@kbn/unified-histogram",
"@kbn/alerts-ui-shared",
"@kbn/core-pricing-browser-mocks"
"@kbn/core-pricing-browser-mocks",
"@kbn/css-utils"
],
"exclude": ["target/**/*"]
}

View file

@ -24,12 +24,11 @@ import {
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 { useKbnFullScreenBgCss } from '@kbn/css-utils/public/full_screen_bg_css';
import { getServices } from '../kibana_services';
import { SampleDataCard } from './sample_data';
@ -44,7 +43,8 @@ interface WelcomeProps {
export const Welcome: React.FC<WelcomeProps> = ({ urlBasePath, onSkip }: WelcomeProps) => {
const services = getServices();
const euiShadowM = useEuiShadow('m');
const theme = useEuiTheme();
const kbnFullScreenBgCss = useKbnFullScreenBgCss();
const redirectToAddData = () => {
services.application.navigateToApp('integrations', { path: '/browse' });
@ -80,13 +80,7 @@ export const Welcome: React.FC<WelcomeProps> = ({ urlBasePath, onSkip }: Welcome
return (
<EuiPortal>
<div
data-test-subj="homeWelcomeInterstitial"
css={[
styles,
fullScreenGraphicsMixinStyles(Number(theme.euiTheme.levels.navigation), theme),
]}
>
<div data-test-subj="homeWelcomeInterstitial" css={[styles, kbnFullScreenBgCss]}>
<header className="homeWelcome__header">
<div className="homeWelcome__content eui-textCenter">
<EuiSpacer size="xl" />

View file

@ -36,6 +36,7 @@
"@kbn/core-http-browser",
"@kbn/deeplinks-observability",
"@kbn/react-kibana-context-theme",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { ElasticRequestState } from '@kbn/unified-doc-viewer';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { useEsDocSearch } from '../../hooks';
import { getHeight, DEFAULT_MARGIN_BOTTOM } from './get_height';
import { JSONCodeEditorCommonMemoized } from '../json_code_editor';
@ -51,7 +51,7 @@ export const DocViewerSource = ({
decreaseAvailableHeightBy,
onRefresh,
}: SourceViewerProps) => {
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>();
const [editorHeight, setEditorHeight] = useState<number>();

View file

@ -38,7 +38,7 @@ import {
canPrependTimeFieldColumn,
} from '@kbn/discover-utils';
import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { getUnifiedDocViewerServices } from '../../plugin';
import {
@ -136,7 +136,7 @@ export const DocViewerTable = ({
onAddColumn,
onRemoveColumn,
}: DocViewRenderProps) => {
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
const isEsqlMode = Array.isArray(textBasedHits);
const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null);

View file

@ -21,7 +21,7 @@ import {
import React, { Fragment, useCallback, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { IgnoredReason } from '@kbn/discover-utils';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
export const DOC_VIEWER_DEFAULT_TRUNCATE_MAX_HEIGHT = 110;
@ -111,7 +111,7 @@ export const TableFieldValue = ({
isDetails,
isHighlighted,
}: TableFieldValueProps) => {
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
const truncationHeight = DOC_VIEWER_DEFAULT_TRUNCATE_MAX_HEIGHT;

View file

@ -46,7 +46,8 @@
"@kbn/embeddable-plugin",
"@kbn/apm-types-shared",
"@kbn/object-utils",
"@kbn/es-query"
"@kbn/es-query",
"@kbn/css-utils",
],
"exclude": ["target/**/*"]
}

View file

@ -1,3 +1,13 @@
@mixin flexParent($grow: 1, $shrink: 1, $basis: auto, $direction: column) {
flex: $grow $shrink $basis;
display: flex;
flex-direction: $direction;
> * {
flex-shrink: 0;
}
}
.visEditor {
height: 100%;
@include flexParent();

View file

@ -716,6 +716,8 @@
"@kbn/crypto/*": ["src/platform/packages/shared/kbn-crypto/*"],
"@kbn/crypto-browser": ["src/platform/packages/shared/kbn-crypto-browser"],
"@kbn/crypto-browser/*": ["src/platform/packages/shared/kbn-crypto-browser/*"],
"@kbn/css-utils": ["src/platform/packages/shared/kbn-css-utils"],
"@kbn/css-utils/*": ["src/platform/packages/shared/kbn-css-utils/*"],
"@kbn/custom-branding-plugin": ["x-pack/platform/plugins/private/custom_branding"],
"@kbn/custom-branding-plugin/*": ["x-pack/platform/plugins/private/custom_branding/*"],
"@kbn/custom-icons": ["src/platform/packages/shared/kbn-custom-icons"],

View file

@ -35,7 +35,6 @@ module.exports = {
},
resolve: {
alias: {
core_app_image_assets: path.resolve(KIBANA_ROOT, 'src/core/public/styles/core_app/images'),
[require.resolve('@elastic/eui/es/components/drag_and_drop')]: false,
},
extensions: ['.js', '.json', '.ts', '.tsx', '.scss'],

View file

@ -7,7 +7,8 @@
import React, { useState, useEffect } from 'react';
import { css } from '@emotion/react';
import { kibanaFullBodyHeightCss, useMemoizedStyles } from '@kbn/core/public';
import { kbnFullBodyHeightCss } from '@kbn/css-utils/public/full_body_height_css';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { EuiFlexGroup, EuiFlexItem, EuiTitle, UseEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { formatRequestPayload, formatJson } from '../lib/format';
@ -41,7 +42,7 @@ const mainStyles = {
// (they're both the same height, hence the x2)
const bodyOffset = `(${bottomBarHeight} * 2)`;
return kibanaFullBodyHeightCss(bodyOffset);
return kbnFullBodyHeightCss(bodyOffset);
},
};
@ -53,7 +54,7 @@ export const Main: React.FunctionComponent = () => {
links,
} = useAppContext();
const styles = useMemoizedStyles(mainStyles);
const styles = useMemoCss(mainStyles);
const [isRequestFlyoutOpen, setRequestFlyoutOpen] = useState(false);
const { inProgress, response, submit } = useSubmitCode(http);

View file

@ -17,7 +17,7 @@ import {
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { Response } from '../../types';
import { OutputTab } from './output_tab';
import { ParametersTab } from './parameters_tab';
@ -49,7 +49,7 @@ interface Props {
}
export const OutputPane: FunctionComponent<Props> = ({ isLoading, response }) => {
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
const outputTabLabel = (
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>

View file

@ -23,6 +23,7 @@
"@kbn/code-editor",
"@kbn/react-kibana-context-render",
"@kbn/scout",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -8,7 +8,7 @@
import React from 'react';
import { UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { getColorPalette, getLinearGradient } from '../../../color_palettes';
interface Props {
@ -29,7 +29,7 @@ const componentStyles = {
};
export const ColorGradient = ({ colorPaletteId }: Props) => {
const palette = getColorPalette(colorPaletteId);
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
return palette.length ? (
<div css={styles.mapColorGradientStyles} style={{ background: getLinearGradient(palette) }} />
) : null;

View file

@ -11,7 +11,7 @@ import { EuiSpacer, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow } from
import { removeRow, isColorInvalid } from './color_stops_utils';
import { i18n } from '@kbn/i18n';
import { MbValidatedColorPicker } from './mb_validated_color_picker';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { mapColorStopsStyles } from '../map_color_stops.styles';
export const ColorStops = ({
@ -23,7 +23,7 @@ export const ColorStops = ({
addNewRow,
swatches,
}) => {
const styles = useMemoizedStyles(mapColorStopsStyles);
const styles = useMemoCss(mapColorStopsStyles);
function getStopInput(stop, index) {
const onStopChange = (newStopValue) => {
const newColorStops = _.cloneDeep(colorStops);

View file

@ -8,7 +8,7 @@
import React, { ReactElement } from 'react';
import { css } from '@emotion/react';
import { EuiFlexGroup, EuiFlexItem, EuiText, UseEuiTheme } from '@elastic/eui';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { VECTOR_STYLES } from '../../../../../../common/constants';
import { VectorIcon } from './vector_icon';
@ -31,7 +31,7 @@ export function Category({
symbolId,
svg,
}: Props) {
const styles = useMemoizedStyles(categoryStyles);
const styles = useMemoCss(categoryStyles);
function renderIcon() {
if (styleName === VECTOR_STYLES.LABEL_COLOR) {

View file

@ -8,7 +8,7 @@
import React from 'react';
import { EuiText, UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { StyleError } from './style_error';
import {
DynamicStyleProperty,
@ -51,7 +51,7 @@ export function VectorStyleLegend({
}: Props) {
const legendRows = [];
const cssStyles = useMemoizedStyles(vectorStyleLegendStyles);
const cssStyles = useMemoCss(vectorStyleLegendStyles);
for (let i = 0; i < styles.length; i++) {
const styleMetaDataRequest = styles[i].isDynamic()

View file

@ -7,7 +7,7 @@
import { css } from '@emotion/react';
import { UseEuiTheme } from '@elastic/eui';
import type { EmotionStyles } from '@kbn/core/public';
import type { EmotionStyles } from '@kbn/css-utils/public/use_memo_css';
export const mapColorStopsStyles: EmotionStyles = {
mapColorStops: ({ euiTheme }: UseEuiTheme) =>

View file

@ -14,7 +14,7 @@ import { IconSelect } from './icon_select';
import { StopInput } from '../stop_input';
import { getMakiSymbol, PREFERRED_ICONS, SYMBOL_OPTIONS } from '../../symbol_utils';
import { mapColorStopsStyles } from '../map_color_stops.styles';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
function isDuplicateStop(targetStop, iconStops) {
const stops = iconStops.filter(({ stop }) => {
@ -53,7 +53,7 @@ export function IconStops({
onCustomIconsChange,
customIcons,
}) {
const styles = useMemoizedStyles(mapColorStopsStyles);
const styles = useMemoCss(mapColorStopsStyles);
return iconStops
.map(({ stop, icon, iconSource }, index) => {
const iconInfo =

View file

@ -7,7 +7,7 @@
import React from 'react';
import { UseEuiTheme } from '@elastic/eui';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { SymbolIcon } from '../legend/symbol_icon';
import { getIsDarkMode } from '../../../../../kibana_services';
@ -18,7 +18,7 @@ const prependButtonStyles = {
};
export const PrependButton = ({ value, svg }: { value: string; svg: string }) => {
const styles = useMemoizedStyles(prependButtonStyles);
const styles = useMemoCss(prependButtonStyles);
return (
<SymbolIcon
key={value}

View file

@ -15,7 +15,7 @@ import { ActionExecutionContext, Action } from '@kbn/ui-actions-plugin/public';
import { Observable } from 'rxjs';
import { ExitFullScreenButton } from '@kbn/shared-ux-button-exit-full-screen';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { MBMap } from '../mb_map';
import { RightSideControls } from '../right_side_controls';
import { Timeslider } from '../timeslider';
@ -269,7 +269,7 @@ const FlyoutPanelWrapper = ({ flyoutDisplay }: { flyoutDisplay: FLYOUT_STATE })
flyoutPanel = <MapSettingsPanel />;
}
const isVisible = !!flyoutPanel;
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
return (
<EuiFlexItem
css={[styles.flyoutPanelWrapperStyles, isVisible && styles.flyoutVisibleStyles]}

View file

@ -38,13 +38,6 @@ exports[`isFullScreen 1`] = `
"_insertTag": [Function],
"before": null,
"container": <head>
<style
data-emotion="css"
data-s=""
>
.css-721pd1-floatTopRight{position:absolute;top:-8px;right:-8px;}
</style>
<style
data-emotion="css"
data-s=""
@ -147,13 +140,6 @@ exports[`render 1`] = `
"_insertTag": [Function],
"before": null,
"container": <head>
<style
data-emotion="css"
data-s=""
>
.css-721pd1-floatTopRight{position:absolute;top:-8px;right:-8px;}
</style>
<style
data-emotion="css"
data-s=""

View file

@ -10,7 +10,7 @@ import React, { useEffect, useState, useCallback } from 'react';
import { UseEuiTheme } from '@elastic/eui';
import type { Map as MapboxMap } from '@kbn/mapbox-gl';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
const MAX_WIDTH = 110;
@ -59,7 +59,7 @@ const componentStyles = {
export const ScaleControl: React.FC<Props> = ({ isFullScreen, mbMap }) => {
const [label, setLabel] = useState('');
const [width, setWidth] = useState(0);
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
const onUpdate = useCallback(() => {
const container = mbMap.getContainer();
const centerHeight = container.clientHeight / 2;

View file

@ -8,7 +8,7 @@
import _ from 'lodash';
import React, { Component, CSSProperties, RefObject, ReactNode } from 'react';
import { css } from '@emotion/react';
import { useMemoizedStyles } from '@kbn/core/public';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import {
EuiCallOut,
EuiLoadingSpinner,
@ -386,7 +386,7 @@ interface MapFeatureTooltipRowProps {
}
export const MapFeatureTooltipRow = ({ children, className }: MapFeatureTooltipRowProps) => {
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
return (
<tr css={styles.mapFeatureTooltipRowStyles} className={className}>
{children}

View file

@ -14,8 +14,8 @@ import {
AppMountParameters,
KibanaExecutionContext,
ScopedHistory,
kibanaFullBodyHeightCss,
} from '@kbn/core/public';
import { kbnFullBodyHeightCss } from '@kbn/css-utils/public/full_body_height_css';
import { Adapters } from '@kbn/inspector-plugin/public';
import { Subscription } from 'rxjs';
import { type Filter, FilterStateStore, type Query, type TimeRange } from '@kbn/es-query';
@ -71,7 +71,7 @@ const styles = {
display: 'flex',
flexDirection: 'column',
},
kibanaFullBodyHeightCss(),
kbnFullBodyHeightCss(),
]),
fullScreen: css({
height: '100vh !important',

View file

@ -95,6 +95,7 @@
"@kbn/react-hooks",
"@kbn/scout",
"@kbn/react-kibana-mount",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -17,8 +17,8 @@ import {
EuiPanel,
UseEuiTheme,
} from '@elastic/eui';
import { kibanaFullBodyHeightCss, useMemoizedStyles } from '@kbn/core/public';
import { kbnFullBodyHeightCss } from '@kbn/css-utils/public/full_body_height_css';
import { useMemoCss } from '@kbn/css-utils/public/use_memo_css';
import { css } from '@emotion/react';
import {
SearchProfilerTabs,
@ -41,7 +41,7 @@ const componentStyles = {
overflow: 'hidden',
flexShrink: 1,
}, // adding dev tool top bar to the body offset
kibanaFullBodyHeightCss(`(${euiTheme.size.base} * 3)`),
kbnFullBodyHeightCss(`(${euiTheme.size.base} * 3)`),
]),
};
@ -103,7 +103,7 @@ export const App = () => {
return null;
};
const styles = useMemoizedStyles(componentStyles);
const styles = useMemoCss(componentStyles);
return (
<>

View file

@ -24,6 +24,7 @@
"@kbn/react-kibana-context-render",
"@kbn/code-editor",
"@kbn/monaco",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -1,45 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AuthenticationStatePage renders 1`] = `
<div
className="secAuthenticationStatePage "
>
<header
className="secAuthenticationStatePage__header"
>
<div
className="secAuthenticationStatePage__content eui-textCenter"
>
<EuiSpacer
size="xxl"
/>
<span
className="secAuthenticationStatePage__logo"
>
<EuiIcon
size="xxl"
type="logoElastic"
/>
</span>
<EuiTitle
className="secAuthenticationStatePage__title"
size="l"
>
<h1>
foo
</h1>
</EuiTitle>
<EuiSpacer
size="xl"
/>
</div>
</header>
<div
className="secAuthenticationStatePage__content eui-textCenter"
>
<span>
hello world
</span>
</div>
</div>
`;

View file

@ -1,7 +1,3 @@
.secAuthenticationStatePage {
@include kibanaFullScreenGraphics;
}
.secAuthenticationStatePage__header {
position: relative;
padding: $euiSizeXL;
@ -11,7 +7,7 @@
.secAuthenticationStatePage__logo {
margin-bottom: $euiSizeXL;
@include kibanaCircleLogo;
display: inline-block;
@include euiBottomShadowMedium;
}

View file

@ -5,30 +5,53 @@
* 2.0.
*/
import { screen } from '@testing-library/react';
import React from 'react';
import { shallowWithIntl } from '@kbn/test-jest-helpers';
import { renderWithI18n } from '@kbn/test-jest-helpers';
import { AuthenticationStatePage } from './authentication_state_page';
describe('AuthenticationStatePage', () => {
it('renders', () => {
expect(
shallowWithIntl(
<AuthenticationStatePage title={'foo'}>
<span>hello world</span>
</AuthenticationStatePage>
)
).toMatchSnapshot();
it('renders the title, child content, and layout elements correctly', () => {
const { container } = renderWithI18n(
<AuthenticationStatePage title="foo">
<span>hello world</span>
</AuthenticationStatePage>
);
// Page wrapper with expected class
const wrapper = container.querySelector('.secAuthenticationStatePage');
expect(wrapper).toBeInTheDocument();
// Header section
const header = container.querySelector('.secAuthenticationStatePage__header');
expect(header).toBeInTheDocument();
// Title in <h1>
const title = screen.getByRole('heading', { level: 1, name: 'foo' });
expect(title).toBeInTheDocument();
// Child content
expect(screen.getByText('hello world')).toBeInTheDocument();
// Icon wrapper with logo class
const logoWrapper = container.querySelector('.secAuthenticationStatePage__logo');
expect(logoWrapper).toBeInTheDocument();
// Check the EuiIcon is rendered with correct type
const icon = logoWrapper?.querySelector('[data-euiicon-type="logoElastic"]');
expect(icon).toBeInTheDocument();
});
it('renders with custom CSS class', () => {
const { container } = renderWithI18n(
<AuthenticationStatePage className="customClassName" title={'foo'}>
<span>hello world</span>
</AuthenticationStatePage>
);
expect(
shallowWithIntl(
<AuthenticationStatePage className="customClassName" title={'foo'}>
<span>hello world</span>
</AuthenticationStatePage>
).exists('.secAuthenticationStatePage.customClassName')
).toBe(true);
container.querySelector('.secAuthenticationStatePage.customClassName')
).toBeInTheDocument();
});
});

View file

@ -11,30 +11,36 @@ import { EuiIcon, EuiImage, EuiSpacer, EuiTitle } from '@elastic/eui';
import type { FC, PropsWithChildren } from 'react';
import React from 'react';
import { useKbnFullScreenBgCss } from '@kbn/css-utils/public/full_screen_bg_css';
interface Props {
className?: string;
title: React.ReactNode;
logo?: string;
}
export const AuthenticationStatePage: FC<PropsWithChildren<Props>> = (props) => (
<div className={`secAuthenticationStatePage ${props.className || ''}`}>
<header className="secAuthenticationStatePage__header">
<div className="secAuthenticationStatePage__content eui-textCenter">
<EuiSpacer size="xxl" />
<span className="secAuthenticationStatePage__logo">
{props.logo ? (
<EuiImage src={props.logo} size={40} alt={'logo'} />
) : (
<EuiIcon type="logoElastic" size="xxl" />
)}
</span>
<EuiTitle size="l" className="secAuthenticationStatePage__title">
<h1>{props.title}</h1>
</EuiTitle>
<EuiSpacer size="xl" />
</div>
</header>
<div className="secAuthenticationStatePage__content eui-textCenter">{props.children}</div>
</div>
);
export const AuthenticationStatePage: FC<PropsWithChildren<Props>> = (props) => {
const kbnFullScreenBgCss = useKbnFullScreenBgCss();
return (
<div className={`secAuthenticationStatePage ${props.className || ''}`} css={kbnFullScreenBgCss}>
<header className="secAuthenticationStatePage__header">
<div className="secAuthenticationStatePage__content eui-textCenter">
<EuiSpacer size="xxl" />
<span className="secAuthenticationStatePage__logo">
{props.logo ? (
<EuiImage src={props.logo} size={40} alt={'logo'} />
) : (
<EuiIcon type="logoElastic" size="xxl" />
)}
</span>
<EuiTitle size="l" className="secAuthenticationStatePage__title">
<h1>{props.title}</h1>
</EuiTitle>
<EuiSpacer size="xl" />
</div>
</header>
<div className="secAuthenticationStatePage__content eui-textCenter">{props.children}</div>
</div>
);
};

View file

@ -293,170 +293,369 @@ exports[`LoginPage enabled form state renders as expected when loginHelp is set
`;
exports[`LoginPage page renders as expected 1`] = `
<div
className="loginWelcome login-form"
>
<header
className="loginWelcome__header"
>
<div
className="loginWelcome__content eui-textCenter"
>
<span
className="loginWelcome__logo"
style={Object {}}
>
<EuiIcon
size="xxl"
type="logoElastic"
/>
</span>
<EuiTitle
className="loginWelcome__title"
data-test-subj="loginWelcomeTitle"
size="m"
>
<h1>
<MemoizedFormattedMessage
defaultMessage="Welcome to Elastic"
id="xpack.security.loginPage.welcomeTitle"
/>
</h1>
</EuiTitle>
</div>
</header>
<div>
<div
className="loginWelcome__content loginWelcome-body"
class="loginWelcome login-form css-1gj8l3e-kbnFullScreenBgCss"
>
<EuiFlexGroup
gutterSize="l"
<header
class="loginWelcome__header"
>
<EuiFlexItem>
<LoginForm
http={
Object {
"addLoadingCountSource": [MockFunction],
"get": [MockFunction],
}
}
loginAssistanceMessage=""
notifications={
Object {
"showErrorDialog": [MockFunction],
"toasts": Object {
"add": [MockFunction],
"addDanger": [MockFunction],
"addError": [MockFunction],
"addInfo": [MockFunction],
"addSuccess": [MockFunction],
"addWarning": [MockFunction],
"get$": [MockFunction],
"remove": [MockFunction],
},
}
}
selector={
Object {
"enabled": false,
"providers": Array [
Object {
"name": "basic1",
"type": "basic",
"usesLoginForm": true,
},
],
}
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<div
class="loginWelcome__content eui-textCenter"
>
<span
class="loginWelcome__logo"
>
<span
data-euiicon-type="logoElastic"
/>
</span>
<h1
class="euiTitle loginWelcome__title emotion-euiTitle-m"
data-test-subj="loginWelcomeTitle"
>
Welcome to Elastic
</h1>
</div>
</header>
<div
class="loginWelcome__content loginWelcome-body"
>
<div
class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row"
>
<div
class="euiFlexItem emotion-euiFlexItem-grow-1"
>
<div
class="euiPanel euiPanel--transparent euiPanel--paddingMedium emotion-euiPanel-grow-m-m-transparent"
data-test-subj="loginForm"
>
<form>
<div
class="euiFormRow euiFormRow--hasLabel emotion-euiFormRow"
id="generated-id-row"
>
<div
class="euiFormRow__labelWrapper"
>
<label
aria-invalid="false"
class="euiFormLabel euiFormRow__label euiFormLabel-isFocused emotion-euiFormLabel-focused"
for="generated-id"
id="generated-id-label"
>
Username
</label>
</div>
<div
class="euiFormRow__fieldWrapper"
>
<div
class="euiFormControlLayout emotion-euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper emotion-euiFormControlLayout__childrenWrapper"
>
<input
aria-required="true"
autocomplete="off"
class="euiFieldText emotion-euiFieldText"
data-test-subj="loginUsername"
id="generated-id"
name="username"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
<div
class="euiFormRow euiFormRow--hasLabel emotion-euiFormRow"
id="generated-id-row"
>
<div
class="euiFormRow__labelWrapper"
>
<label
aria-invalid="false"
class="euiFormLabel euiFormRow__label emotion-euiFormLabel"
for="generated-id"
id="generated-id-label"
>
Password
</label>
</div>
<div
class="euiFormRow__fieldWrapper"
>
<div
class="euiFormControlLayout euiFormControlLayout--group emotion-euiFormControlLayout-group"
>
<div
class="euiFormControlLayout__childrenWrapper emotion-euiFormControlLayout__childrenWrapper-inGroup-appendOnly"
style="--euiFormControlLeftIconsCount: 1;"
>
<div
class="euiFormControlLayoutIcons emotion-euiFormControlLayoutIcons-absolute-left"
>
<span
class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon"
>
<span
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="lock"
/>
</span>
</div>
<input
aria-required="true"
autocomplete="off"
class="euiFieldPassword emotion-euiFieldPassword-inGroup-withToggle"
data-test-subj="loginPassword"
id="generated-id"
name="password"
type="password"
value=""
/>
</div>
<div
class="euiFormControlLayout__append emotion-euiFormControlLayout__side-append"
>
<button
aria-label="Show password as plain text. Note: this will visually expose your password on the screen."
class="euiButtonIcon emotion-euiButtonIcon-xs-empty-primary"
title="Show password as plain text. Note: this will visually expose your password on the screen."
type="button"
>
<span
aria-hidden="true"
class="euiButtonIcon__icon"
color="inherit"
data-euiicon-type="eye"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="euiSpacer euiSpacer--l emotion-euiSpacer-l"
/>
<div
class="euiFlexGroup emotion-euiFlexGroup-s-flexStart-center-row"
>
<div
class="euiFlexItem emotion-euiFlexItem-grow-1"
>
<button
class="euiButton emotion-euiButtonDisplay-m-fullWidth-defaultMinWidth-fill-primary"
data-test-subj="loginSubmit"
type="submit"
>
<span
class="emotion-euiButtonDisplayContent"
>
Log in
</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`LoginPage page renders with custom branding 1`] = `
<div
className="loginWelcome login-form"
>
<header
className="loginWelcome__header"
>
<div
className="loginWelcome__content eui-textCenter"
>
<span
className="loginWelcome__logo"
style={
Object {
"padding": 0,
}
}
>
<EuiImage
alt="logo"
size={40}
src="logo"
/>
</span>
<EuiTitle
className="loginWelcome__title"
data-test-subj="loginWelcomeTitle"
size="m"
>
<h1>
<MemoizedFormattedMessage
defaultMessage="Welcome to Elastic"
id="xpack.security.loginPage.welcomeTitle"
/>
</h1>
</EuiTitle>
</div>
</header>
<div>
<div
className="loginWelcome__content loginWelcome-body"
class="loginWelcome login-form css-1gj8l3e-kbnFullScreenBgCss"
>
<EuiFlexGroup
gutterSize="l"
<header
class="loginWelcome__header"
>
<EuiFlexItem>
<LoginForm
http={
Object {
"addLoadingCountSource": [MockFunction],
"get": [MockFunction],
}
}
loginAssistanceMessage=""
notifications={
Object {
"showErrorDialog": [MockFunction],
"toasts": Object {
"add": [MockFunction],
"addDanger": [MockFunction],
"addError": [MockFunction],
"addInfo": [MockFunction],
"addSuccess": [MockFunction],
"addWarning": [MockFunction],
"get$": [MockFunction],
"remove": [MockFunction],
},
}
}
selector={
Object {
"enabled": false,
"providers": Array [
Object {
"name": "basic1",
"type": "basic",
"usesLoginForm": true,
},
],
}
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<div
class="loginWelcome__content eui-textCenter"
>
<span
class="loginWelcome__logo"
style="padding: 0px;"
>
<figure
class="euiImageWrapper emotion-euiImageWrapper"
>
<img
alt="logo"
class="euiImage emotion-euiImage-customSize"
src="logo"
style="max-width: 40px; max-height: 40px;"
/>
</figure>
</span>
<h1
class="euiTitle loginWelcome__title emotion-euiTitle-m"
data-test-subj="loginWelcomeTitle"
>
Welcome to Elastic
</h1>
</div>
</header>
<div
class="loginWelcome__content loginWelcome-body"
>
<div
class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row"
>
<div
class="euiFlexItem emotion-euiFlexItem-grow-1"
>
<div
class="euiPanel euiPanel--transparent euiPanel--paddingMedium emotion-euiPanel-grow-m-m-transparent"
data-test-subj="loginForm"
>
<form>
<div
class="euiFormRow euiFormRow--hasLabel emotion-euiFormRow"
id="generated-id-row"
>
<div
class="euiFormRow__labelWrapper"
>
<label
aria-invalid="false"
class="euiFormLabel euiFormRow__label euiFormLabel-isFocused emotion-euiFormLabel-focused"
for="generated-id"
id="generated-id-label"
>
Username
</label>
</div>
<div
class="euiFormRow__fieldWrapper"
>
<div
class="euiFormControlLayout emotion-euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper emotion-euiFormControlLayout__childrenWrapper"
>
<input
aria-required="true"
autocomplete="off"
class="euiFieldText emotion-euiFieldText"
data-test-subj="loginUsername"
id="generated-id"
name="username"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
<div
class="euiFormRow euiFormRow--hasLabel emotion-euiFormRow"
id="generated-id-row"
>
<div
class="euiFormRow__labelWrapper"
>
<label
aria-invalid="false"
class="euiFormLabel euiFormRow__label emotion-euiFormLabel"
for="generated-id"
id="generated-id-label"
>
Password
</label>
</div>
<div
class="euiFormRow__fieldWrapper"
>
<div
class="euiFormControlLayout euiFormControlLayout--group emotion-euiFormControlLayout-group"
>
<div
class="euiFormControlLayout__childrenWrapper emotion-euiFormControlLayout__childrenWrapper-inGroup-appendOnly"
style="--euiFormControlLeftIconsCount: 1;"
>
<div
class="euiFormControlLayoutIcons emotion-euiFormControlLayoutIcons-absolute-left"
>
<span
class="euiFormControlLayoutCustomIcon emotion-euiFormControlLayoutCustomIcon"
>
<span
aria-hidden="true"
class="euiFormControlLayoutCustomIcon__icon"
data-euiicon-type="lock"
/>
</span>
</div>
<input
aria-required="true"
autocomplete="off"
class="euiFieldPassword emotion-euiFieldPassword-inGroup-withToggle"
data-test-subj="loginPassword"
id="generated-id"
name="password"
type="password"
value=""
/>
</div>
<div
class="euiFormControlLayout__append emotion-euiFormControlLayout__side-append"
>
<button
aria-label="Show password as plain text. Note: this will visually expose your password on the screen."
class="euiButtonIcon emotion-euiButtonIcon-xs-empty-primary"
title="Show password as plain text. Note: this will visually expose your password on the screen."
type="button"
>
<span
aria-hidden="true"
class="euiButtonIcon__icon"
color="inherit"
data-euiicon-type="eye"
/>
</button>
</div>
</div>
</div>
</div>
<div
class="euiSpacer euiSpacer--l emotion-euiSpacer-l"
/>
<div
class="euiFlexGroup emotion-euiFlexGroup-s-flexStart-center-row"
>
<div
class="euiFlexItem emotion-euiFlexItem-grow-1"
>
<button
class="euiButton emotion-euiButtonDisplay-m-fullWidth-defaultMinWidth-fill-primary"
data-test-subj="loginSubmit"
type="submit"
>
<span
class="emotion-euiButtonDisplayContent"
>
Log in
</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -1,7 +1,3 @@
.loginWelcome {
@include kibanaFullScreenGraphics;
}
.loginWelcome__header {
margin-top: calc($euiSizeXXL * 3);
position: relative;
@ -11,8 +7,7 @@
.loginWelcome__logo {
margin-bottom: $euiSizeXL;
@include kibanaCircleLogo;
display: inline-block;
}
.loginWelcome__content {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { EuiFlexItem } from '@elastic/eui';
import { EuiFlexItem, EuiThemeProvider } from '@elastic/eui';
import { act } from '@testing-library/react';
import { shallow } from 'enzyme';
import React from 'react';
@ -13,7 +13,7 @@ import { of } from 'rxjs';
import { coreMock } from '@kbn/core/public/mocks';
import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks';
import { nextTick } from '@kbn/test-jest-helpers';
import { nextTick, renderWithI18n } from '@kbn/test-jest-helpers';
import { DisabledLoginForm, LoginForm, LoginFormMessageType } from './components';
import { LoginPage } from './login_page';
@ -61,23 +61,23 @@ describe('LoginPage', () => {
customBrandingMock.customBranding$ = of({});
httpMock.get.mockResolvedValue(createLoginState());
const wrapper = shallow(
const { container } = renderWithI18n(
<LoginPage
http={httpMock}
customBranding={customBrandingMock}
notifications={coreStartMock.notifications}
fatalErrors={coreStartMock.fatalErrors}
loginAssistanceMessage=""
/>
/>,
{ wrapper: EuiThemeProvider }
);
await act(async () => {
await nextTick();
wrapper.update();
resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot
});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
it('renders with custom branding', async () => {
@ -85,23 +85,23 @@ describe('LoginPage', () => {
customBrandingMock.customBranding$ = of({ logo: 'logo' });
httpMock.get.mockResolvedValue(createLoginState());
const wrapper = shallow(
const { container } = renderWithI18n(
<LoginPage
http={httpMock}
customBranding={customBrandingMock}
notifications={coreStartMock.notifications}
fatalErrors={coreStartMock.fatalErrors}
loginAssistanceMessage=""
/>
/>,
{ wrapper: EuiThemeProvider }
);
await act(async () => {
await nextTick();
wrapper.update();
resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot
});
expect(wrapper).toMatchSnapshot();
expect(container).toMatchSnapshot();
});
});

View file

@ -31,6 +31,7 @@ import type {
NotificationsStart,
} from '@kbn/core/public';
import type { CustomBranding } from '@kbn/core-custom-branding-common';
import { kbnFullScreenBgCss } from '@kbn/css-utils/public/full_screen_bg_css';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
@ -154,7 +155,7 @@ export class LoginPage extends Component<Props, State> {
// custom logo needs to be centered
const logoStyle = customLogo ? { padding: 0 } : {};
return (
<div className="loginWelcome login-form">
<div className="loginWelcome login-form" css={kbnFullScreenBgCss}>
<header className="loginWelcome__header">
<div className={contentHeaderClasses}>
<span className="loginWelcome__logo" style={logoStyle}>

View file

@ -2,7 +2,7 @@
exports[`OverwrittenSessionPage renders as expected 1`] = `
<div
class="secAuthenticationStatePage "
class="secAuthenticationStatePage css-1vgeo0r-kbnFullScreenBgCss"
>
<header
class="secAuthenticationStatePage__header"

View file

@ -8,7 +8,7 @@
"public/**/*",
"server/**/*",
"__mocks__/**/*",
"../../../../../typings/emotion.d.ts"
"../../../../../typings/**/*",
],
"kbn_references": [
"@kbn/cloud-plugin",
@ -93,6 +93,7 @@
"@kbn/core-http-server-utils",
"@kbn/core-user-profile-browser-mocks",
"@kbn/react-kibana-context-theme",
"@kbn/css-utils",
],
"exclude": [
"target/**/*",

View file

@ -5,12 +5,7 @@ exports[`it renders with custom logo 1`] = `
css="unknown styles"
data-test-subj="kibanaSpaceSelector"
>
<EuiPortal>
<div
className="spcSelectorBackground spcSelectorBackground__nonMixinAttributes"
role="presentation"
/>
</EuiPortal>
<Memo(BackgroundPortal) />
<_EuiPageSection
color="transparent"
paddingSize="xl"
@ -65,12 +60,7 @@ exports[`it renders without crashing 1`] = `
css="unknown styles"
data-test-subj="kibanaSpaceSelector"
>
<EuiPortal>
<div
className="spcSelectorBackground spcSelectorBackground__nonMixinAttributes"
role="presentation"
/>
</EuiPortal>
<Memo(BackgroundPortal) />
<_EuiPageSection
color="transparent"
paddingSize="xl"

View file

@ -1,7 +1,3 @@
.spcSelectorBackground {
@include kibanaFullScreenGraphics;
}
.spcSelectorBackground__nonMixinAttributes {
z-index: -1;
pointer-events: none;

View file

@ -25,6 +25,7 @@ import type { Observable, Subscription } from 'rxjs';
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import type { CustomBranding } from '@kbn/core-custom-branding-common';
import { useKbnFullScreenBgCss } from '@kbn/css-utils/public/full_screen_bg_css';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution';
@ -130,14 +131,7 @@ export class SpaceSelector extends Component<Props, State> {
`}
data-test-subj="kibanaSpaceSelector"
>
{/* Portal the fixed background graphic so it doesn't affect page positioning or overlap on top of global banners */}
<EuiPortal>
<div
className="spcSelectorBackground spcSelectorBackground__nonMixinAttributes"
role="presentation"
/>
</EuiPortal>
<BackgroundPortal />
<KibanaPageTemplate.Section color="transparent" paddingSize="xl">
<EuiText textAlign="center" size="s">
<EuiSpacer size="xxl" />
@ -278,3 +272,17 @@ export const renderSpaceSelectorApp = (
ReactDOM.render(services.rendering.addContext(<SpaceSelector {...props} />), element);
return () => ReactDOM.unmountComponentAtNode(element);
};
// portal the fixed background graphic so it doesn't affect page positioning or overlap on top of global banners
const BackgroundPortal = React.memo(function BackgroundPortal() {
const kbnFullScreenBgCss = useKbnFullScreenBgCss();
return (
<EuiPortal>
<div
className="spcSelectorBackground spcSelectorBackground__nonMixinAttributes"
css={kbnFullScreenBgCss}
role="presentation"
/>
</EuiPortal>
);
});

View file

@ -55,7 +55,8 @@
"@kbn/core-chrome-browser",
"@kbn/core-lifecycle-server",
"@kbn/core-user-profile-browser-mocks",
"@kbn/spaces-utils"
"@kbn/spaces-utils",
"@kbn/css-utils"
],
"exclude": ["target/**/*"]
}

View file

@ -5203,6 +5203,10 @@
version "0.0.0"
uid ""
"@kbn/css-utils@link:src/platform/packages/shared/kbn-css-utils":
version "0.0.0"
uid ""
"@kbn/custom-branding-plugin@link:x-pack/platform/plugins/private/custom_branding":
version "0.0.0"
uid ""