Skip to content

Commit

Permalink
Enable to provide ReactNode as header quick access item #141
Browse files Browse the repository at this point in the history
  • Loading branch information
garronej committed Jun 13, 2023
1 parent e6ed104 commit 8a9a8fc
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 70 deletions.
109 changes: 56 additions & 53 deletions src/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ export const Footer = memo(
assert<Equals<keyof typeof rest, never>>();

const { brandTop, homeLinkProps } = (() => {

const wrap = getBrandTopAndHomeLinkProps();

const brandTop = brandTop_prop ?? wrap?.brandTop;
const homeLinkProps = homeLinkProps_prop ?? wrap?.homeLinkProps;

const exceptionMessage = " hasn't been provided to the Footer and we cannot retrieve it from the Header (it's probably client side)";
const exceptionMessage =
" hasn't been provided to the Footer and we cannot retrieve it from the Header (it's probably client side)";

if (brandTop === undefined) {
throw new Error(symToStr({ brandTop }) + exceptionMessage);
Expand All @@ -187,11 +187,9 @@ export const Footer = memo(
throw new Error(symToStr({ homeLinkProps }) + exceptionMessage);
}

return { brandTop, homeLinkProps};

return { brandTop, homeLinkProps };
})();


const { Link } = getLink();

const { t } = useTranslation();
Expand Down Expand Up @@ -428,76 +426,81 @@ export const Footer = memo(
...(websiteMapLinkProps === undefined
? []
: [
id<FooterProps.BottomItem>({
"text": t("website map"),
"linkProps": websiteMapLinkProps
})
]),
id<FooterProps.BottomItem>({
"text": t("website map"),
"linkProps": websiteMapLinkProps
})
]),
id<FooterProps.BottomItem>({
"text": `${t("accessibility")}: ${t(accessibility)}`,
"linkProps": accessibilityLinkProps ?? {}
}),
...(termsLinkProps === undefined
? []
: [
id<FooterProps.BottomItem>({
"text": t("terms"),
"linkProps": termsLinkProps
})
]),
id<FooterProps.BottomItem>({
"text": t("terms"),
"linkProps": termsLinkProps
})
]),
...(personalDataLinkProps === undefined
? []
: [
id<FooterProps.BottomItem>({
"text": t("personal data"),
"linkProps": personalDataLinkProps
})
]),
id<FooterProps.BottomItem>({
"text": t("personal data"),
"linkProps": personalDataLinkProps
})
]),
...(cookiesManagementButtonProps === undefined
? // one or the other, but not both. Priority to button for consent modal control.
cookiesManagementLinkProps === undefined
cookiesManagementLinkProps === undefined
? []
: [
id<FooterProps.BottomItem>({
"text": t("cookies management"),
"linkProps": cookiesManagementLinkProps
})
]
id<FooterProps.BottomItem>({
"text": t("cookies management"),
"linkProps": cookiesManagementLinkProps
})
]
: [
id<FooterProps.BottomItem>({
"text": t("cookies management"),
"buttonProps":
cookiesManagementButtonProps.nativeButtonProps
})
]),
id<FooterProps.BottomItem>({
"text": t("cookies management"),
"buttonProps":
cookiesManagementButtonProps.nativeButtonProps
})
]),
...bottomItems
].map((bottomItem, i) =>
<li className={cx(fr.cx("fr-footer__bottom-item"), classes.bottomItem, className)} key={i}>
{
!typeGuard<FooterProps.BottomItem>(
bottomItem,
bottomItem instanceof Object && "text" in bottomItem
) ? (
bottomItem
) : (
<FooterBottomItem
classes={{
"bottomLink": classes.bottomLink
}}
bottomItem={bottomItem}
/>
)
}
].map((bottomItem, i) => (
<li
className={cx(
fr.cx("fr-footer__bottom-item"),
classes.bottomItem,
className
)}
key={i}
>
{!typeGuard<FooterProps.BottomItem>(
bottomItem,
bottomItem instanceof Object && "text" in bottomItem
) ? (
bottomItem
) : (
<FooterBottomItem
classes={{
"bottomLink": classes.bottomLink
}}
bottomItem={bottomItem}
/>
)}
</li>
)}
))}
</ul>
<div className={cx(fr.cx("fr-footer__bottom-copy"), classes.bottomCopy)}>
<p>
{license === undefined
? t("license mention", {
"licenseUrl":
"https://github.com/etalab/licence-ouverte/blob/master/LO.md"
})
"licenseUrl":
"https://github.com/etalab/licence-ouverte/blob/master/LO.md"
})
: license}
</p>
</div>
Expand Down
59 changes: 44 additions & 15 deletions src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { MainNavigationProps } from "./MainNavigation";
import { MainNavigation } from "./MainNavigation";
import { Display } from "./Display/Display";
import { setBrandTopAndHomeLinkProps } from "./zz_internal/brandTopAndHomeLinkProps";
import { typeGuard } from "tsafe/typeGuard";

export type HeaderProps = {
className?: string;
Expand All @@ -21,7 +22,7 @@ export type HeaderProps = {
serviceTagline?: ReactNode;
navigation?: MainNavigationProps.Item[] | ReactNode;
/** There should be at most three of them */
quickAccessItems?: HeaderProps.QuickAccessItem[];
quickAccessItems?: (HeaderProps.QuickAccessItem | ReactNode)[];
operatorLogo?: {
orientation: "horizontal" | "vertical";
/**
Expand Down Expand Up @@ -125,22 +126,15 @@ export const Header = memo(

const quickAccessNode = (
<ul className={fr.cx("fr-btns-group")}>
{quickAccessItems.map(({ iconId, text, buttonProps, linkProps }, i) => (
{quickAccessItems.map((quickAccessItem, i) => (
<li key={i}>
{linkProps !== undefined ? (
<Link
{...linkProps}
className={cx(fr.cx("fr-btn", iconId), linkProps.className)}
>
{text}
</Link>
{!typeGuard<HeaderProps.QuickAccessItem>(
quickAccessItem,
quickAccessItem instanceof Object && "text" in quickAccessItem
) ? (
quickAccessItem
) : (
<button
{...buttonProps}
className={cx(fr.cx("fr-btn", iconId), buttonProps.className)}
>
{text}
</button>
<HeaderQuickAccessItem quickAccessItem={quickAccessItem} />
)}
</li>
))}
Expand Down Expand Up @@ -406,3 +400,38 @@ addHeaderTranslations({
});

export { addHeaderTranslations };

export type HeaderQuickAccessItemProps = {
className?: string;
quickAccessItem: HeaderProps.QuickAccessItem;
};

export function HeaderQuickAccessItem(props: HeaderQuickAccessItemProps): JSX.Element {
const { className, quickAccessItem } = props;

const { Link } = getLink();

return quickAccessItem.linkProps !== undefined ? (
<Link
{...quickAccessItem.linkProps}
className={cx(
fr.cx("fr-btn", quickAccessItem.iconId),
quickAccessItem.linkProps.className,
className
)}
>
{quickAccessItem.text}
</Link>
) : (
<button
{...quickAccessItem.buttonProps}
className={cx(
fr.cx("fr-btn", quickAccessItem.iconId),
quickAccessItem.buttonProps.className,
className
)}
>
{quickAccessItem.text}
</button>
);
}
9 changes: 8 additions & 1 deletion stories/Header.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ const { meta, getStory } = getStoryFactory({
- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/en-tete)
- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Header.tsx)
See also [\\<MainNavigation \\/\\>](https://react-dsfr-components.etalab.studio/?path=/docs/components-mainnavigation)`,
See also [\\<MainNavigation \\/\\>](https://react-dsfr-components.etalab.studio/?path=/docs/components-mainnavigation)
> Note for Next App Router: If you want to have \`quickAccessItems\` client side without having to wrap the whole \`<Header />\`
> component within a \`"use client";\` directive you can use the \`<HeaderQuickAccessItem />\` component as demonstrated
[here](https://github.com/codegouvfr/react-dsfr/blob/703961e480eb5f8d39e571fdd64de725aa1d4ff9/test/integration/next-appdir/app/layout.tsx#L91) and
[here](https://github.com/codegouvfr/react-dsfr/blob/703961e480eb5f8d39e571fdd64de725aa1d4ff9/test/integration/next-appdir/ui/ClientHeaderQuickAccessItem.tsx#L1-L18).
`,
"argTypes": {
"brandTop": {
"control": { "type": null },
Expand Down
4 changes: 3 additions & 1 deletion test/integration/next-appdir/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { fr } from "@codegouvfr/react-dsfr";
import { Navigation } from "./Navigation";
import Link from "next/link";
import { ClientFooterItem } from "../ui/ClientFooterItem";
import { ClientHeaderQuickAccessItem } from "../ui/ClientHeaderQuickAccessItem";

declare module "@codegouvfr/react-dsfr/gdpr" {
interface RegisterGdprServices {
Expand Down Expand Up @@ -86,7 +87,8 @@ export default function RootLayout({ children }: { children: JSX.Element; }) {
href: `mailto:${"joseph.garrone@code.gouv.fr"}`,
},
text: "Nous contacter",
}
},
<ClientHeaderQuickAccessItem />
]}
navigation={<Navigation />}
/>
Expand Down
18 changes: 18 additions & 0 deletions test/integration/next-appdir/ui/ClientHeaderQuickAccessItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import { HeaderQuickAccessItem } from "@codegouvfr/react-dsfr/Header";

export function ClientHeaderQuickAccessItem() {
return (
<HeaderQuickAccessItem
quickAccessItem={{
iconId: "fr-icon-article-fill",
linkProps: {
href: `/some-page`,
},
text: "A client side item",

}}
/>
);
}

0 comments on commit 8a9a8fc

Please sign in to comment.