diff --git a/packages/eui/changelogs/upcoming/9211.md b/packages/eui/changelogs/upcoming/9211.md new file mode 100644 index 00000000000..0f1c822414f --- /dev/null +++ b/packages/eui/changelogs/upcoming/9211.md @@ -0,0 +1 @@ +- Added `--euiBottomBarOffset` CSS variable to `EuiBottomBar` for positioning other fixed elements relative to the bottom bar's height diff --git a/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx b/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx index 4b2264e4e0f..1d69c1eec58 100644 --- a/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx +++ b/packages/eui/src/components/bottom_bar/bottom_bar.test.tsx @@ -89,4 +89,77 @@ describe('EuiBottomBar', () => { expect(baseElement).toMatchSnapshot(); }); }); + + describe('CSS variables', () => { + test('sets the correct css variable for the bottom bar', () => { + const { unmount } = render( + + Content + + ); + + // The CSS variable should be set on the document root + const cssVarValue = getComputedStyle( + document.documentElement + ).getPropertyValue('--euiBottomBarOffset'); + expect(cssVarValue).toBeTruthy(); + expect(cssVarValue).toMatch(/\d+px/); + + unmount(); + + // After unmounting, the CSS variable should be cleared + const clearedValue = getComputedStyle( + document.documentElement + ).getPropertyValue('--euiBottomBarOffset'); + expect(clearedValue.trim()).toBe(''); + }); + + test('does not set css variable when affordForDisplacement is false', () => { + render( + Content + ); + + const cssVarValue = getComputedStyle( + document.documentElement + ).getPropertyValue('--euiBottomBarOffset'); + expect(cssVarValue.trim()).toBe(''); + }); + + test('does not set css variable when usePortal is false', () => { + render( + + Content + + ); + + const cssVarValue = getComputedStyle( + document.documentElement + ).getPropertyValue('--euiBottomBarOffset'); + expect(cssVarValue.trim()).toBe(''); + }); + + test('does not set css variable for non-fixed positions', () => { + const { unmount: unmountSticky } = render( + Content + ); + + let cssVarValue = getComputedStyle( + document.documentElement + ).getPropertyValue('--euiBottomBarOffset'); + expect(cssVarValue.trim()).toBe(''); + + unmountSticky(); + + const { unmount: unmountStatic } = render( + Content + ); + + cssVarValue = getComputedStyle(document.documentElement).getPropertyValue( + '--euiBottomBarOffset' + ); + expect(cssVarValue.trim()).toBe(''); + + unmountStatic(); + }); + }); }); diff --git a/packages/eui/src/components/bottom_bar/bottom_bar.tsx b/packages/eui/src/components/bottom_bar/bottom_bar.tsx index 806d3742950..e0c95d954ca 100644 --- a/packages/eui/src/components/bottom_bar/bottom_bar.tsx +++ b/packages/eui/src/components/bottom_bar/bottom_bar.tsx @@ -18,6 +18,7 @@ import React, { import { useCombinedRefs, useEuiMemoizedStyles, + useEuiThemeCSSVariables, EuiThemeProvider, } from '../../services'; import { EuiScreenReaderOnly } from '../accessibility'; @@ -120,6 +121,7 @@ const _EuiBottomBar = forwardRef< ref ) => { const styles = useEuiMemoizedStyles(euiBottomBarStyles); + const { setGlobalCSSVariables } = useEuiThemeCSSVariables(); // Force some props if `fixed` position, but not if the user has supplied these affordForDisplacement = @@ -134,6 +136,11 @@ const _EuiBottomBar = forwardRef< useEffect(() => { if (affordForDisplacement && usePortal) { document.body.style.paddingBottom = `${dimensions.height}px`; + + // EUI doesn't use this css variable, but it is useful for consumers + setGlobalCSSVariables({ + '--euiBottomBarOffset': `${dimensions.height}px`, + }); } if (bodyClassName) { @@ -143,13 +150,22 @@ const _EuiBottomBar = forwardRef< return () => { if (affordForDisplacement && usePortal) { document.body.style.paddingBottom = ''; + setGlobalCSSVariables({ + '--euiBottomBarOffset': null, + }); } if (bodyClassName) { document.body.classList.remove(bodyClassName); } }; - }, [affordForDisplacement, usePortal, dimensions, bodyClassName]); + }, [ + affordForDisplacement, + usePortal, + dimensions, + bodyClassName, + setGlobalCSSVariables, + ]); const classes = classNames( 'euiBottomBar', diff --git a/packages/website/docs/components/containers/bottom-bar.mdx b/packages/website/docs/components/containers/bottom-bar.mdx index aa398ad02aa..88d4cc838c0 100644 --- a/packages/website/docs/components/containers/bottom-bar.mdx +++ b/packages/website/docs/components/containers/bottom-bar.mdx @@ -209,6 +209,21 @@ export default () => { }; ``` +### Relative positioning + +When `affordForDisplacement` is enabled and the bottom bar is positioned as `fixed`, **EuiBottomBar** automatically sets a global CSS variable that you can use to position other fixed elements in your application relative to the bottom bar's height: + +- `--euiBottomBarOffset` (Set when `affordForDisplacement={true}` and `position="fixed"`) + +This variable is automatically updated when the bottom bar resizes and is removed when the bottom bar is closed. + +```css +.custom-fixed-footer { + /* Adjust footer position when bottom bar is visible */ + padding-bottom: var(--euiBottomBarOffset, 0); +} +``` + ## Props import docgen from '@elastic/eui-docgen/dist/components/bottom_bar';