Skip to content

Commit

Permalink
[EuiHeader] Set up CSS variable for fixed header affordance; [Emotion…
Browse files Browse the repository at this point in the history
…] Set up CSS variable architecture (#7151)

Co-authored-by: Trevor Pierce <[email protected]>
  • Loading branch information
cee-chen and 1Copenut authored Sep 5, 2023
1 parent a588a35 commit 829709e
Show file tree
Hide file tree
Showing 35 changed files with 655 additions and 223 deletions.
17 changes: 0 additions & 17 deletions src-docs/src/components/guide_page/_guide_page.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,5 @@
@import '../../../../src/global_styling/mixins/helpers';

@include euiHeaderAffordForFixed;

.guideBody {
// Override euiHeaderAffordForFixed mixin since the page template handles this now
padding-top: 0 !important; // stylelint-disable-line declaration-no-important
}

.euiBody--headerIsFixed--double {
@include euiHeaderAffordForFixed($euiHeaderHeightCompensation * 2);

.euiHeader:not([data-fixed-header]) {
// Force headers below the fullscreen.
// This shouldn't be necessary in consuming applications because headers should always be at the top of the page
z-index: 0;
}
}

.guideSideNav {
@include euiSideNavEmbellish;
}
Expand Down
2 changes: 1 addition & 1 deletion src-docs/src/components/guide_page/guide_page_chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class GuidePageChrome extends Component {
scrollNavSectionIntoView = () => {
// wait a bit for react to blow away and re-create the DOM
// then scroll the selected nav section into view
requestAnimationFrame(() => {
setTimeout(() => {
const sideNav = document.querySelector('.guideSideNav__content');
const isMobile = sideNav?.querySelector('.euiSideNav__mobileToggle');

Expand Down
10 changes: 1 addition & 9 deletions src-docs/src/views/header/header_elastic_pattern.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';

import {
Expand Down Expand Up @@ -53,14 +53,6 @@ export default () => {
prefix: 'guideHeaderDeploymentPopover',
});

useEffect(() => {
document.body.classList.add('euiBody--headerIsFixed--double');

return () => {
document.body.classList.remove('euiBody--headerIsFixed--double');
};
}, []);

/**
* Collapsible Nav
*/
Expand Down
40 changes: 23 additions & 17 deletions src-docs/src/views/header/header_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,24 +298,35 @@ export const HeaderExample = {
<>
<p>
Most consumers need a header that does not scroll away with the page
contents. You can apply this display by applying the property{' '}
<EuiCode language="ts">{'position="fixed"'}</EuiCode>. This will
also add a class of <EuiCode>.euiBody--headerIsFixed</EuiCode> to
the window body.
contents. You can set this display by applying the property{' '}
<EuiCode language="ts">{'position="fixed"'}</EuiCode>. Multiple
fixed headers will automatically stack underneath one another. No
manual positioning is required.
</p>
<p>
You will then need to apply your own padding to this body class to
afford for the header height. EUI supplies a helper mixin that also
accounts for this height in flyouts and the collapsible nav. Simply
add{' '}
<EuiCode language="sass">@include euiHeaderAffordForFixed;</EuiCode>{' '}
anywhere in your SASS.
If you're using{' '}
<Link to="/templates/page-template">
<strong>EuiPageTemplate</strong>
</Link>
, a padding top will be automatically set based on the number of
fixed headers on the page.{' '}
<Link to="/layout/flyout">
<strong>EuiFlyouts</strong>
</Link>{' '}
will also automatically account for and sit below fixed headers.
</p>
<p>
If you're using your own custom layout, or have custom UI that needs
to sit below your fixed header(s), EUI provides a global CSS{' '}
<EuiCode language="css">var(--euiFixedHeadersOffset)</EuiCode>{' '}
variable. You can use this variable anywhere, or even override it,
to correctly offset any and all fixed header heights.
</p>
</>
),
snippet: [
'<EuiHeader position="fixed" />',
'@include euiHeaderAffordForFixed;',
'body { padding-block-start: var(--euiFixedHeadersOffset, 0) }',
],
demo: <HeaderPosition />,
demoPanelProps: {
Expand Down Expand Up @@ -446,17 +457,12 @@ export const HeaderExample = {
text: (
<p>
Stacking multiple headers provides a great way to separate global
navigation concerns. However, the{' '}
<EuiCode language="ts">{'position="fixed"'}</EuiCode> option will not
be aware of the number of headers. If you do need fixed{' '}
<strong>and</strong> stacked headers, you will need to apply the SASS
helper mixin and pass in the correct height to afford for.
navigation concerns.
</p>
),
snippet: [
`<EuiHeader theme="dark" position="fixed" />
<EuiHeader position="fixed" />`,
'@include euiHeaderAffordForFixed($euiHeaderHeightCompensation * 2);',
],
demo: <HeaderStacked />,
demoPanelProps: {
Expand Down
10 changes: 1 addition & 9 deletions src-docs/src/views/header/header_stacked.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';

import {
EuiBreadcrumb,
Expand Down Expand Up @@ -27,14 +27,6 @@ export default () => {
},
];

useEffect(() => {
if (isFixed) document.body.classList.add('euiBody--headerIsFixed--double');

return () => {
document.body.classList.remove('euiBody--headerIsFixed--double');
};
}, [isFixed]);

const headers = (
<>
<EuiHeader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ export const MultipleFixedHeaders: Story = {
<EuiHeader position="fixed">
<EuiHeaderSection>
<EuiCollapsibleNavBeta {...args}>
This story tests that EuiCollapsibleNav's fixed header detection &
offsetting works as expected
This story tests that EuiCollapsibleNav automatically adjusts its
position & height for multiple fixed headers
</EuiCollapsibleNavBeta>
Second header
</EuiHeaderSection>
Expand Down
16 changes: 13 additions & 3 deletions src/components/collapsible_nav_beta/collapsible_nav_beta.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,29 @@ import { css } from '@emotion/react';
import { UseEuiTheme } from '../../services';
import { logicalCSS, euiYScroll } from '../../global_styling';
import { euiShadowFlat } from '../../themes';
import { euiHeaderVariables } from '../header/header.styles';

export const euiCollapsibleNavBetaStyles = (euiThemeContext: UseEuiTheme) => {
const { euiTheme } = euiThemeContext;

// At least for serverless, EuiCollapsibleNav is only going to be used with 1
// fixed header. For those scenarios, we can prevent a minor layout jump on
// page load by setting the CSS var fallback to the height of a single header
const defaultHeaderHeight = euiHeaderVariables(euiThemeContext).height;
const fixedHeaderOffset = `var(--euiFixedHeadersOffset, ${defaultHeaderHeight})`;

return {
euiCollapsibleNavBeta: css`
/* Fixed header affordance */
${logicalCSS('top', fixedHeaderOffset)}
/* Allow the nav to scroll, in case consumers don't use EuiFlyoutBody/EuiFyoutFooter */
${euiYScroll(euiThemeContext, { height: 'inherit' })}
/* This extra padding is needed for EuiPopovers to have enough
space to render with the right anchorPosition */
${logicalCSS('padding-bottom', euiTheme.size.xs)}
/* Allow the nav to scroll, in case consumers don't use EuiFlyoutBody/EuiFyoutFooter */
${euiYScroll(euiThemeContext)}
/* In case things get really dire responsively, ensure the footer doesn't overtake the body */
.euiFlyoutBody {
${logicalCSS('min-height', '50%')}
Expand Down
16 changes: 0 additions & 16 deletions src/components/collapsible_nav_beta/collapsible_nav_beta.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { render } from '../../test/rtl';
import { shouldRenderCustomStyles } from '../../test/internal';
import { requiredProps } from '../../test';

import { EuiHeader } from '../header';

import { EuiCollapsibleNavBeta } from './collapsible_nav_beta';

describe('EuiCollapsibleNavBeta', () => {
Expand Down Expand Up @@ -66,20 +64,6 @@ describe('EuiCollapsibleNavBeta', () => {
expect(onCollapseToggle).toHaveBeenLastCalledWith(true);
});

it('automatically accounts for fixed EuiHeaders in its positioning', () => {
const { getByTestSubject } = render(
<EuiHeader position="fixed">
<EuiCollapsibleNavBeta data-test-subj="nav">
Nav content
</EuiCollapsibleNavBeta>
</EuiHeader>
);
expect(getByTestSubject('nav')).toHaveStyle({
'inset-block-start': '48px',
'block-size': 'calc(100% - 48px)',
});
});

describe('responsive behavior', () => {
const mockWindowResize = (width: number) => {
window.innerWidth = width;
Expand Down
32 changes: 1 addition & 31 deletions src/components/collapsible_nav_beta/collapsible_nav_beta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import React, {
import classNames from 'classnames';

import { useEuiTheme, useGeneratedHtmlId, throttle } from '../../services';
import { mathWithUnits, logicalStyle } from '../../global_styling';

import { CommonProps } from '../common';
import { EuiFlyout, EuiFlyoutProps } from '../flyout';
Expand Down Expand Up @@ -83,7 +82,6 @@ export const EuiCollapsibleNavBeta: FunctionComponent<
id,
children,
className,
style,
initialIsCollapsed = false,
onCollapseToggle,
width: _width = 248,
Expand Down Expand Up @@ -144,32 +142,6 @@ export const EuiCollapsibleNavBeta: FunctionComponent<
return _width;
}, [_width, isOverlayFullWidth, isPush, isCollapsed, headerHeight]);

/**
* Header affordance
*/
const [fixedHeadersCount, setFixedHeadersCount] = useState<number | false>(
false
);
useEffect(() => {
setFixedHeadersCount(
document.querySelectorAll('.euiHeader[data-fixed-header]').length
);
}, []);

const stylesWithHeaderOffset = useMemo(() => {
if (!fixedHeadersCount) return style;

const headersOffset = mathWithUnits(
headerHeight,
(x) => x * fixedHeadersCount
);
return {
...style,
...logicalStyle('top', headersOffset),
...logicalStyle('height', `calc(100% - ${headersOffset})`),
};
}, [fixedHeadersCount, style, headerHeight]);

/**
* Prop setup
*/
Expand Down Expand Up @@ -205,15 +177,13 @@ export const EuiCollapsibleNavBeta: FunctionComponent<
isOverlayFullWidth && styles.isOverlayFullWidth,
];

// Wait for any fixed headers to be queried before rendering (prevents position jumping)
const flyout = fixedHeadersCount !== false && (
const flyout = (
<EuiFlyout
aria-label={defaultAriaLabel}
{...rest} // EuiCollapsibleNav is significantly less permissive than EuiFlyout
id={flyoutID}
css={cssStyles}
className={classes}
style={stylesWithHeaderOffset}
size={width}
side={side}
focusTrapProps={focusTrapProps}
Expand Down
15 changes: 13 additions & 2 deletions src/components/datagrid/_data_grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,23 @@
}

// This is used to remove extra scrollbars on the body when fullscreen is enabled
// and tweak any components that account for fixed headers
.euiDataGrid__restrictBody {
height: 100vh;
overflow: hidden;

.euiHeader {
z-index: $euiZHeaderBelowDataGrid;
.euiHeader[data-fixed-header] {
// !important needed to override header inline styles
z-index: $euiZHeaderBelowDataGrid !important; // stylelint-disable-line declaration-no-important
}

.euiOverlayMask[data-relative-to-header='below'] {
top: 0;
}

.euiFlyout {
top: 0;
height: 100%;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/flyout/__snapshots__/flyout.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1086,12 +1086,12 @@ Array [
exports[`EuiFlyout renders extra screen reader instructions when fixed EuiHeaders headers exist on the page 1`] = `
<body
class="euiBody--headerIsFixed euiBody--hasFlyout"
data-fixed-headers="1"
>
<div>
<div
class="euiHeader emotion-euiHeader-fixed-default"
data-fixed-header="true"
style="inset-block-start: 0px;"
/>
<div>
<div
Expand Down
4 changes: 2 additions & 2 deletions src/components/flyout/flyout.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ export const euiFlyoutStyles = (euiThemeContext: UseEuiTheme) => {
return {
euiFlyout: css`
position: fixed;
${logicalCSS('top', 0)}
${logicalCSS('bottom', 0)}
${logicalCSS('height', '100%')}
${logicalCSS('top', 'var(--euiFixedHeadersOffset, 0)')}
${logicalCSS('height', 'inherit')}
z-index: ${euiTheme.levels.flyout};
background: ${euiTheme.colors.emptyShade};
display: flex;
Expand Down
1 change: 1 addition & 0 deletions src/components/header/__snapshots__/header.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ exports[`EuiHeader renders in fixed position 1`] = `
<div
class="euiHeader emotion-euiHeader-fixed-default"
data-fixed-header="true"
style="inset-block-start: 0px;"
>
<span>
Hello!
Expand Down
Loading

0 comments on commit 829709e

Please sign in to comment.