Skip to content

Commit

Permalink
[EuiCollapsibleNavBeta] Store mobile overlay open/close state separat…
Browse files Browse the repository at this point in the history
…ely from desktop collapsed/expanded state (#7319)
  • Loading branch information
cee-chen authored Oct 26, 2023
1 parent 393a91d commit 5bf77d2
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`EuiCollapsibleNavBeta renders 1`] = `
<button
aria-controls="generated-id_euiCollapsibleNav"
aria-expanded="true"
aria-label="Toggle navigation closed"
aria-label="Collapse navigation"
aria-pressed="true"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand Down Expand Up @@ -65,7 +65,7 @@ exports[`EuiCollapsibleNavBeta renders initialIsCollapsed 1`] = `
<button
aria-controls="generated-id_euiCollapsibleNav"
aria-expanded="false"
aria-label="Toggle navigation open"
aria-label="Expand navigation"
aria-pressed="false"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand Down
28 changes: 24 additions & 4 deletions src/components/collapsible_nav_beta/collapsible_nav_beta.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import { fireEvent } from '@testing-library/react';
import { fireEvent, waitFor } from '@testing-library/react';
import { render } from '../../test/rtl';
import { shouldRenderCustomStyles } from '../../test/internal';
import { requiredProps } from '../../test';
Expand Down Expand Up @@ -84,6 +84,24 @@ describe('EuiCollapsibleNavBeta', () => {
expect(getByTestSubject('nav').className).not.toContain('push');
});

it('stores push collapsed/expand and overlay flyout open/closed states separately', () => {
mockWindowResize(1200);
const { queryByTestSubject, getByTestSubject } = render(
<EuiCollapsibleNavBeta data-test-subj="nav">
Nav content
</EuiCollapsibleNavBeta>
);
expect(getByTestSubject('nav')).toHaveStyle({ 'inline-size': '248px' });

// Should be closed on mobile
mockWindowResize(600);
waitFor(() => expect(queryByTestSubject('nav')).not.toBeInTheDocument());

// Should still be expanded on desktop
mockWindowResize(1200);
expect(getByTestSubject('nav')).toHaveStyle({ 'inline-size': '248px' });
});

it('makes the overlay flyout full width once the screen is smaller than 1.5x the flyout width', () => {
mockWindowResize(320);
const { baseElement, getByTestSubject } = render(
Expand All @@ -100,9 +118,11 @@ describe('EuiCollapsibleNavBeta', () => {
baseElement.querySelector('[data-euiicon-type="cross"')
).toBeInTheDocument();
fireEvent.keyDown(window, { key: 'Escape' });
expect(
baseElement.querySelector('[data-euiicon-type="menu"')
).toBeInTheDocument();
waitFor(() =>
expect(
baseElement.querySelector('[data-euiicon-type="menu"')
).toBeInTheDocument()
);
});

it('adjusts breakpoints for custom widths', () => {
Expand Down
18 changes: 9 additions & 9 deletions src/components/collapsible_nav_beta/collapsible_nav_beta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ const _EuiCollapsibleNavBeta: FunctionComponent<EuiCollapsibleNavBetaProps> = ({
*/
const [isOverlay, setIsOverlay] = useState(false);
const [isOverlayFullWidth, setIsOverlayFullWidth] = useState(false);
const [isOverlayOpen, setIsOverlayOpen] = useState(false);
const toggleOverlayFlyout = useCallback(() => {
setIsOverlayOpen((isOpen) => !isOpen);
}, []);

const flyoutType = isOverlay ? 'overlay' : 'push';
const isPush = !isOverlay;
Expand All @@ -135,12 +139,6 @@ const _EuiCollapsibleNavBeta: FunctionComponent<EuiCollapsibleNavBetaProps> = ({
return () => window.removeEventListener('resize', onWindowResize);
}, [_width]);

// If the nav was previously uncollapsed and shrinks down to the
// overlay flyout, default to its hidden/collapsed state
useEffect(() => {
if (isOverlay) setIsCollapsed(true);
}, [isOverlay]);

const width = useMemo(() => {
if (isOverlayFullWidth) return '100%';
if (isPush && isCollapsed) return headerHeight;
Expand Down Expand Up @@ -211,13 +209,15 @@ const _EuiCollapsibleNavBeta: FunctionComponent<EuiCollapsibleNavBetaProps> = ({
</EuiFlyout>
);

const hideFlyout = isOverlay && isCollapsed;
const hideFlyout = isOverlay && !isOverlayOpen;

return (
<EuiCollapsibleNavContext.Provider value={{ isPush, isCollapsed, side }}>
<EuiCollapsibleNavContext.Provider
value={{ isPush, isCollapsed, isOverlayOpen, side }}
>
<EuiCollapsibleNavButton
ref={buttonRef}
onClick={toggleCollapsed}
onClick={isPush ? toggleCollapsed : toggleOverlayFlyout}
aria-controls={flyoutID}
/>
{!hideFlyout && flyout}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ describe('EuiCollapsibleNavBody', () => {
it('renders with docked styles', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isPush: true, isCollapsed: true }}
value={{
isPush: true,
isCollapsed: true,
isOverlayOpen: false,
side: 'left',
}}
>
<EuiCollapsibleNavBody {...requiredProps} />
</EuiCollapsibleNavContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exports[`EuiCollapsibleNavButton overlay flyout renders a hamburger icon when co
>
<button
aria-expanded="false"
aria-label="Toggle navigation open"
aria-label="Open navigation"
aria-pressed="false"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand All @@ -28,7 +28,7 @@ exports[`EuiCollapsibleNavButton overlay flyout renders an X icon when expanded
>
<button
aria-expanded="true"
aria-label="Toggle navigation closed"
aria-label="Close navigation"
aria-pressed="true"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand All @@ -50,7 +50,7 @@ exports[`EuiCollapsibleNavButton push flyout left side renders a menu left icon
>
<button
aria-expanded="true"
aria-label="Toggle navigation closed"
aria-label="Collapse navigation"
aria-pressed="true"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand All @@ -72,7 +72,7 @@ exports[`EuiCollapsibleNavButton push flyout left side renders a menu right icon
>
<button
aria-expanded="false"
aria-label="Toggle navigation open"
aria-label="Expand navigation"
aria-pressed="false"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand All @@ -94,7 +94,7 @@ exports[`EuiCollapsibleNavButton push flyout right side renders a menu left icon
>
<button
aria-expanded="false"
aria-label="Toggle navigation open"
aria-label="Expand navigation"
aria-pressed="false"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand All @@ -116,7 +116,7 @@ exports[`EuiCollapsibleNavButton push flyout right side renders a menu right ico
>
<button
aria-expanded="true"
aria-label="Toggle navigation closed"
aria-label="Collapse navigation"
aria-pressed="true"
class="euiButtonIcon euiCollapsibleNavButton emotion-euiButtonIcon-s-empty-text-euiCollapsibleNavButton"
data-test-subj="euiCollapsibleNavButton"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders a menu left icon when expanded', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isPush: true, isCollapsed: false }}
value={{
side: 'left',
isPush: true,
isCollapsed: false,
isOverlayOpen: false,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand All @@ -44,7 +49,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders a menu right icon when collapsed', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isPush: true, isCollapsed: true }}
value={{
side: 'left',
isPush: true,
isCollapsed: true,
isOverlayOpen: false,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand All @@ -61,7 +71,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders a menu right icon when expanded', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'right', isPush: true, isCollapsed: false }}
value={{
side: 'right',
isPush: true,
isCollapsed: false,
isOverlayOpen: false,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand All @@ -76,7 +91,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders a menu left icon when collapsed', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'right', isPush: true, isCollapsed: true }}
value={{
side: 'right',
isPush: true,
isCollapsed: true,
isOverlayOpen: false,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand All @@ -94,7 +114,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders an X icon when expanded', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isPush: false, isCollapsed: false }}
value={{
isOverlayOpen: true,
isPush: false,
side: 'left',
isCollapsed: false,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand All @@ -109,7 +134,12 @@ describe('EuiCollapsibleNavButton', () => {
it('renders a hamburger icon when collapsed', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'right', isPush: false, isCollapsed: true }}
value={{
isOverlayOpen: false,
isPush: false,
side: 'right',
isCollapsed: true,
}}
>
<EuiCollapsibleNavButton />
</EuiCollapsibleNavContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export const EuiCollapsibleNavButton = forwardRef<
HTMLDivElement,
EuiCollapsibleNavButtonProps
>(({ className, css, ...rest }, ref) => {
const { side, isPush, isCollapsed } = useContext(EuiCollapsibleNavContext);
const { side, isPush, isCollapsed, isOverlayOpen } = useContext(
EuiCollapsibleNavContext
);

const euiTheme = useEuiTheme();
const styles = euiCollapsibleNavButtonWrapperStyles(euiTheme);
Expand All @@ -33,27 +35,37 @@ export const EuiCollapsibleNavButton = forwardRef<
const buttonStyles = [styles.euiCollapsibleNavButton, css];
const classes = classNames('euiCollapsibleNavButton', className);

const toggleExpandLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelExpand',
'Expand navigation'
);
const toggleCollapseLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelCollapse',
'Collapse navigation'
);
const toggleOpenLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelOpen',
'Open navigation'
);
const toggleCloseLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelClose',
'Close navigation'
);

let ariaLabel: string;
let iconType: string;
if (isPush) {
ariaLabel = isCollapsed ? toggleExpandLabel : toggleCollapseLabel;
if (side === 'left') {
iconType = isCollapsed ? 'menuRight' : 'menuLeft';
} else {
iconType = isCollapsed ? 'menuLeft' : 'menuRight';
}
} else {
iconType = isCollapsed ? 'menu' : 'cross';
ariaLabel = isOverlayOpen ? toggleCloseLabel : toggleOpenLabel;
iconType = isOverlayOpen ? 'cross' : 'menu';
}

const toggleOpenLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelOpen',
'Toggle navigation open'
);
const toggleCloselLabel = useEuiI18n(
'euiCollapsibleNavButton.ariaLabelClose',
'Toggle navigation closed'
);
const ariaLabel = isCollapsed ? toggleOpenLabel : toggleCloselLabel;

return (
<div className="euiCollapsibleNavButtonWrapper" css={cssStyles} ref={ref}>
<EuiButtonIcon
Expand All @@ -64,8 +76,8 @@ export const EuiCollapsibleNavButton = forwardRef<
color="text"
iconType={iconType}
aria-label={ariaLabel}
aria-pressed={!isCollapsed}
aria-expanded={!isCollapsed}
aria-pressed={isPush ? !isCollapsed : isOverlayOpen}
aria-expanded={isPush ? !isCollapsed : isOverlayOpen}
{...rest}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ describe('EuiCollapsibleNavGroup', () => {
it('renders as a docked button icon', () => {
const { container } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isCollapsed: true, isPush: true }}
value={{
isCollapsed: true,
isPush: true,
isOverlayOpen: false,
side: 'left',
}}
>
<EuiCollapsibleNavGroup {...sharedProps} />
</EuiCollapsibleNavContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ describe('EuiCollapsibleNavItem', () => {
it('renders a collapsed button icon when in a collapsed push flyout', () => {
const { container, getByTestSubject } = render(
<EuiCollapsibleNavContext.Provider
value={{ side: 'left', isPush: true, isCollapsed: true }}
value={{
isPush: true,
isCollapsed: true,
isOverlayOpen: false,
side: 'left',
}}
>
<EuiCollapsibleNavItem {...requiredProps} title="Item" />
</EuiCollapsibleNavContext.Provider>
Expand Down
2 changes: 2 additions & 0 deletions src/components/collapsible_nav_beta/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import { _EuiFlyoutSide } from '../flyout/flyout';
type _EuiCollapsibleNavContext = {
isCollapsed: boolean;
isPush: boolean;
isOverlayOpen: boolean;
side: _EuiFlyoutSide;
};

export const EuiCollapsibleNavContext =
createContext<_EuiCollapsibleNavContext>({
isCollapsed: false,
isPush: true,
isOverlayOpen: false,
side: 'left',
});

0 comments on commit 5bf77d2

Please sign in to comment.