Skip to content

Commit 43b0951

Browse files
committed
Add a hook to observe if the header modal menu is open or not
1 parent cee75a9 commit 43b0951

File tree

6 files changed

+58
-22
lines changed

6 files changed

+58
-22
lines changed

src/Header.tsx renamed to src/Header/Header.tsx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import React, { memo, forwardRef, useId, type ReactNode, type CSSProperties } from "react";
2-
import { fr } from "./fr";
3-
import { createComponentI18nApi } from "./i18n";
2+
import { fr } from "../fr";
3+
import { createComponentI18nApi } from "../i18n";
44
import { symToStr } from "tsafe/symToStr";
5-
import { cx } from "./tools/cx";
6-
import { getLink } from "./link";
7-
import type { RegisteredLinkProps } from "./link";
5+
import { cx } from "../tools/cx";
6+
import { getLink } from "../link";
7+
import type { RegisteredLinkProps } from "../link";
88
import { assert } from "tsafe/assert";
99
import type { Equals } from "tsafe";
10-
import type { FrIconClassName, RiIconClassName } from "./fr/generatedFromCss/classNames";
11-
import type { MainNavigationProps } from "./MainNavigation";
12-
import { MainNavigation } from "./MainNavigation";
13-
import { Display } from "./Display/Display";
14-
import { setBrandTopAndHomeLinkProps } from "./zz_internal/brandTopAndHomeLinkProps";
10+
import type { FrIconClassName, RiIconClassName } from "../fr/generatedFromCss/classNames";
11+
import type { MainNavigationProps } from "../MainNavigation";
12+
import { MainNavigation } from "../MainNavigation";
13+
import { Display } from "../Display/Display";
14+
import { setBrandTopAndHomeLinkProps } from "../zz_internal/brandTopAndHomeLinkProps";
1515
import { typeGuard } from "tsafe/typeGuard";
16-
import { SearchButton } from "./SearchBar/SearchButton";
17-
import { useTranslation as useSearchBarTranslation } from "./SearchBar/SearchBar";
16+
import { SearchButton } from "../SearchBar/SearchButton";
17+
import { useTranslation as useSearchBarTranslation } from "../SearchBar/SearchBar";
1818

1919
export type HeaderProps = {
2020
className?: string;
@@ -96,6 +96,8 @@ export namespace HeaderProps {
9696
}
9797
}
9898

99+
export const headerMenuModalId = "header-menu-modal";
100+
99101
/** @see <https://components.react-dsfr.fr/?path=/docs/components-header> */
100102
export const Header = memo(
101103
forwardRef<HTMLDivElement, HeaderProps>((props, ref) => {
@@ -122,17 +124,15 @@ export const Header = memo(
122124

123125
setBrandTopAndHomeLinkProps({ brandTop, homeLinkProps });
124126

125-
const { menuButtonId, menuModalId, searchModalId, searchInputId } = (function useClosure() {
127+
const { menuButtonId, searchModalId, searchInputId } = (function useClosure() {
126128
const id = useId();
127129

128130
const menuButtonId = `button-${id}`;
129-
const menuModalId = `modal-${id}`;
130131
const searchModalId = `modal-${id}`;
131132
const searchInputId = `search-${id}-input`;
132133

133134
return {
134135
menuButtonId,
135-
menuModalId,
136136
searchModalId,
137137
searchInputId
138138
};
@@ -253,7 +253,7 @@ export const Header = memo(
253253
<button
254254
className={fr.cx("fr-btn--menu", "fr-btn")}
255255
data-fr-opened="false"
256-
aria-controls={menuModalId}
256+
aria-controls={headerMenuModalId}
257257
aria-haspopup="menu"
258258
id={menuButtonId}
259259
title={t("menu")}
@@ -372,13 +372,13 @@ export const Header = memo(
372372
{(navigation !== undefined || quickAccessItems.length !== 0) && (
373373
<div
374374
className={cx(fr.cx("fr-header__menu", "fr-modal"), classes.menu)}
375-
id={menuModalId}
375+
id={headerMenuModalId}
376376
aria-labelledby={menuButtonId}
377377
>
378378
<div className={fr.cx("fr-container")}>
379379
<button
380380
className={fr.cx("fr-btn--close", "fr-btn")}
381-
aria-controls={menuModalId}
381+
aria-controls={headerMenuModalId}
382382
title={t("close")}
383383
>
384384
{t("close")}

src/Header/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export { default } from "./Header";
2+
export {
3+
Header,
4+
type HeaderProps,
5+
HeaderQuickAccessItem,
6+
type HeaderQuickAccessItemProps,
7+
addHeaderTranslations,
8+
useTranslation
9+
} from "./Header";
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { headerMenuModalId } from "./Header";
2+
import { useIsModalOpen } from "../Modal/useIsModalOpen";
3+
4+
export function useIsHeaderMenuModalOpen() {
5+
return useIsModalOpen({
6+
"id": headerMenuModalId,
7+
"isOpenedByDefault": false
8+
});
9+
}

stories/Header.stories.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,20 @@ const { meta, getStory } = getStoryFactory({
1212
"wrappedComponent": { Header },
1313
"description": `
1414
- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/en-tete)
15-
- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Header.tsx))
15+
- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Header.tsx)
1616
17-
See also [\\<MainNavigation \\/\\>](https://components.react-dsfr.fr/?path=/docs/components-mainnavigation)
17+
See also [\\<MainNavigation \\/\\>](https://components.react-dsfr.fr/?path=/docs/components-mainnavigation)
18+
19+
*NOTE*: On small screens (mobile), you can click on the burger menu to open the menu modal.
20+
You can watch if the menu modal is open or not with the \`useIsHeaderMenuModalOpen\` hook.
21+
22+
\`\`\`tsx
23+
24+
import { useIsHeaderMenuModalOpen } from "@codegouvfr/react-dsfr/Header/useIsHeaderMenuModalOpen";
25+
26+
const isOpen = useIsHeaderMenuModalOpen();
1827
28+
\`\`\`
1929
2030
`,
2131
"argTypes": {

stories/getStory.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,11 @@ export function getStoryFactory<Props extends Record<string, any>>(params: {
149149
...(doHideImportInstruction
150150
? []
151151
: [
152-
`\`import { ${componentName} } from "@codegouvfr/react-dsfr/${componentName}"\``
152+
`\`\`\`tsx `,
153+
` `,
154+
`import { ${componentName} } from "@codegouvfr/react-dsfr/${componentName}"\``,
155+
` `,
156+
`\`\`\``
153157
]),
154158
...(description === undefined ? [] : [description])
155159
].join(" \n")

test/integration/next-appdir/ui/ClientComponent.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ import Button from '@mui/material/Button';
66
import { useIsDark } from "@codegouvfr/react-dsfr/useIsDark";
77
import { createModal } from "@codegouvfr/react-dsfr/Modal";
88
import { useIsModalOpen } from "@codegouvfr/react-dsfr/Modal/useIsModalOpen";
9+
import { useIsHeaderMenuModalOpen } from "@codegouvfr/react-dsfr/Header/useIsHeaderMenuModalOpen";
910

1011
export function ClientComponent() {
1112

1213
const { isDark } = useIsDark();
1314

1415
const isModalOpen = useIsModalOpen(modal);
1516

16-
//console.log(`Modal ${modal.id} is currently: ${isModalOpen ? "open" : "closed"}`);
17+
const isHeaderMenuModalOpen = useIsHeaderMenuModalOpen();
18+
19+
console.log(`Modal ${modal.id} is currently: ${isModalOpen ? "open" : "closed"}`);
20+
console.log(`Header Modal is currently: ${isHeaderMenuModalOpen ? "open" : "closed"}`);
1721

1822
return (
1923
<>

0 commit comments

Comments
 (0)