diff --git a/packages/eui/changelogs/upcoming/8872.md b/packages/eui/changelogs/upcoming/8872.md new file mode 100644 index 00000000000..0a09eee4f28 --- /dev/null +++ b/packages/eui/changelogs/upcoming/8872.md @@ -0,0 +1 @@ +- Added `--euiPushFlyoutOffsetInlineStart` and `--euiPushFlyoutOffsetInlineEnd` global CSS variables set by the `EuiFlyout` in `push` mode. diff --git a/packages/eui/cypress/support/component.tsx b/packages/eui/cypress/support/component.tsx index 32a7ee9de52..e30ee10b7d2 100644 --- a/packages/eui/cypress/support/component.tsx +++ b/packages/eui/cypress/support/component.tsx @@ -23,6 +23,7 @@ import './keyboard/repeatRealPress'; import './copy/select_and_copy'; import './setup/mount'; import './setup/realMount'; +import './css/cssVar'; // @see https://github.com/quasarframework/quasar/issues/2233#issuecomment-492975745 // @see also https://github.com/cypress-io/cypress/issues/20341 diff --git a/packages/eui/cypress/support/css/cssVar.ts b/packages/eui/cypress/support/css/cssVar.ts new file mode 100644 index 00000000000..8bf5bcbb158 --- /dev/null +++ b/packages/eui/cypress/support/css/cssVar.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +Cypress.Commands.add('cssVar', { prevSubject: true }, (subject, name) => { + const value = getComputedStyle(subject[0]).getPropertyValue(name).trim(); + return cy.wrap(value === '' ? null : value); +}); diff --git a/packages/eui/cypress/support/index.d.ts b/packages/eui/cypress/support/index.d.ts index be6cf56bcc4..f049bc0227b 100644 --- a/packages/eui/cypress/support/index.d.ts +++ b/packages/eui/cypress/support/index.d.ts @@ -56,6 +56,12 @@ declare global { * @returns a chainable .then((string) => { doSomethingWith(string); }) */ selectAndCopy(selectorToCopy: string): Chainable; + + /* + * Get the value of a CSS variable from the element's computed styles. + * Params: variableName - the name of the CSS variable (e.g. '--euiColorPrimary') + */ + cssVar(variableName: string): Chainable; } } } diff --git a/packages/eui/src/components/flyout/flyout.spec.tsx b/packages/eui/src/components/flyout/flyout.spec.tsx index 09ac641051f..8dcb04436c1 100644 --- a/packages/eui/src/components/flyout/flyout.spec.tsx +++ b/packages/eui/src/components/flyout/flyout.spec.tsx @@ -399,4 +399,58 @@ describe('EuiFlyout', () => { cy.focused().should('have.class', 'euiFlyout'); }); }); + + describe('css variables', () => { + const euiPushFlyoutOffsetInlineEnd = '--euiPushFlyoutOffsetInlineEnd'; + const euiPushFlyoutOffsetInlineStart = '--euiPushFlyoutOffsetInlineStart'; + + it('sets the correct css variables for the flyout', () => { + cy.mount( + + ); + + cy.get(':root').cssVar(euiPushFlyoutOffsetInlineEnd).should('not.exist'); + cy.get(':root') + .cssVar(euiPushFlyoutOffsetInlineStart) + .should('not.exist'); + + cy.get('[data-test-subj="flyoutSpecTrigger"]').click(); + + cy.get(':root') + .cssVar(euiPushFlyoutOffsetInlineStart) + .should('not.exist'); + cy.get(':root') + .cssVar(euiPushFlyoutOffsetInlineEnd) + .should('eq', '100px'); + + cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); + + cy.get(':root').cssVar(euiPushFlyoutOffsetInlineEnd).should('not.exist'); + cy.get(':root') + .cssVar(euiPushFlyoutOffsetInlineStart) + .should('not.exist'); + + cy.mount( + + ); + + cy.get('[data-test-subj="flyoutSpecTrigger"]').click(); + + cy.get(':root') + .cssVar(euiPushFlyoutOffsetInlineStart) + .should('eq', '100px'); + cy.get(':root').cssVar(euiPushFlyoutOffsetInlineEnd).should('not.exist'); + }); + }); }); diff --git a/packages/eui/src/components/flyout/flyout.tsx b/packages/eui/src/components/flyout/flyout.tsx index 7d6fbf917a3..f1fd33ae3f5 100644 --- a/packages/eui/src/components/flyout/flyout.tsx +++ b/packages/eui/src/components/flyout/flyout.tsx @@ -32,6 +32,7 @@ import { useIsWithinMinBreakpoint, useEuiMemoizedStyles, useGeneratedHtmlId, + useEuiThemeCSSVariables, } from '../../services'; import { logicalStyle } from '../../global_styling'; @@ -217,6 +218,8 @@ export const EuiFlyout = forwardRef( ...rest } = usePropsWithComponentDefaults('EuiFlyout', props); + const { setGlobalCSSVariables } = useEuiThemeCSSVariables(); + const Element = as || defaultElement; const maskRef = useRef(null); @@ -293,13 +296,24 @@ export const EuiFlyout = forwardRef( if (isPushed) { const paddingSide = side === 'left' ? 'paddingInlineStart' : 'paddingInlineEnd'; + const cssVarName = `--euiPushFlyoutOffset${ + side === 'left' ? 'InlineStart' : 'InlineEnd' + }`; document.body.style[paddingSide] = `${width}px`; + + // EUI doesn't use this css variable, but it is useful for consumers + setGlobalCSSVariables({ + [cssVarName]: `${width}px`, + }); return () => { document.body.style[paddingSide] = ''; + setGlobalCSSVariables({ + [cssVarName]: null, + }); }; } - }, [isPushed, side, width]); + }, [isPushed, setGlobalCSSVariables, side, width]); /** * This class doesn't actually do anything by EUI, but is nice to add for consumers (JIC) diff --git a/packages/eui/src/services/theme/provider.tsx b/packages/eui/src/services/theme/provider.tsx index e526bd8fb19..9ebd08deced 100644 --- a/packages/eui/src/services/theme/provider.tsx +++ b/packages/eui/src/services/theme/provider.tsx @@ -272,7 +272,15 @@ export const EuiThemeProvider = ({ const [themeCSSVariables, _setThemeCSSVariables] = useState(); const setThemeCSSVariables = useCallback( (variables: CSSObject) => - _setThemeCSSVariables((previous) => ({ ...previous, ...variables })), + _setThemeCSSVariables((previous) => { + const merged = { ...previous, ...variables }; + Object.keys(merged).forEach((key) => { + if (merged[key] === null) { + delete merged[key]; + } + }); + return merged; + }), [] );