Skip to content

Commit 5bbea88

Browse files
committed
Feature compatibility with various routing library
1 parent 8dd0e64 commit 5bbea88

File tree

6 files changed

+87
-20
lines changed

6 files changed

+87
-20
lines changed

src/Header.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { createComponentI18nApi } from "./lib/i18n";
55
import { symToStr } from "tsafe/symToStr";
66
import { cx } from "./lib/tools/cx";
77
import { FrIconClassName, RiIconClassName } from "./lib/generatedFromCss/classNames";
8+
import type { LinkProps } from "./lib/routing";
9+
import { useLink } from "./lib/routing";
810

911
//NOTE: This is a work in progress, this component is not yet usable.
1012

@@ -15,7 +17,7 @@ export type HeaderProps = {
1517
baselinePrécisionsSurLorganisation: ReactNode;
1618
links: {
1719
text: ReactNode;
18-
href: string;
20+
linkProps: LinkProps;
1921
iconId: FrIconClassName | RiIconClassName;
2022
}[];
2123
};
@@ -35,6 +37,8 @@ export function Header(props: HeaderProps) {
3537

3638
const { t } = useTranslation();
3739

40+
const { Link } = useLink();
41+
3842
return (
3943
<header role="banner" className={cx(fr.cx("fr-header"), className)}>
4044
<div /*className={fr.cx("fr-header__body" as any)}*/>
@@ -75,11 +79,14 @@ export function Header(props: HeaderProps) {
7579
<div className={fr.cx("fr-header__tools")}>
7680
<div className={fr.cx("fr-header__tools-links")}>
7781
<ul className={fr.cx("fr-btns-group")}>
78-
{links.map(({ href, iconId, text }) => (
79-
<li key={href + iconId}>
80-
<a className={fr.cx("fr-btn", iconId)} href={href}>
82+
{links.map(({ linkProps, iconId, text }, i) => (
83+
<li key={i}>
84+
<Link
85+
{...linkProps}
86+
className={fr.cx("fr-btn", iconId)}
87+
>
8188
{text}
82-
</a>
89+
</Link>
8390
</li>
8491
))}
8592
</ul>

src/lib/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export type { SpacingToken } from "./spacing";
88
import { cx } from "./cx";
99
export type { FrCxArg } from "./cx";
1010
export { DsfrLangProvider } from "./i18n";
11+
export { createLinkProvider } from "./routing";
12+
export type { LinkProps, HTMLAnchorProps } from "./routing";
1113

1214
export const fr = {
1315
breakpoints,

src/lib/routing.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React, { createContext, useContext } from "react";
2+
import type { ReactNode } from "react";
3+
4+
import type { DetailedHTMLProps, AnchorHTMLAttributes } from "react";
5+
6+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
7+
export interface LinkProps {
8+
className?: string;
9+
children?: ReactNode;
10+
}
11+
12+
export type HTMLAnchorProps = DetailedHTMLProps<
13+
AnchorHTMLAttributes<HTMLAnchorElement>,
14+
HTMLAnchorElement
15+
>;
16+
17+
const context = createContext<CreateLinkProviderPrams["Link"]>(props => <a {...props} />);
18+
19+
type CreateLinkProviderPrams = {
20+
Link: (props: LinkProps) => JSX.Element;
21+
};
22+
23+
export function createLinkProvider(params: CreateLinkProviderPrams) {
24+
const { Link } = params;
25+
26+
type Props = {
27+
children: ReactNode;
28+
};
29+
30+
function LinkProvider(props: Props) {
31+
const { children } = props;
32+
33+
return <context.Provider value={Link}>{children}</context.Provider>;
34+
}
35+
36+
return { LinkProvider };
37+
}
38+
39+
export function useLink() {
40+
const Link = useContext(context);
41+
42+
return { Link };
43+
}

src/next.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import {
3131
import type { ColorScheme } from "./lib/darkMode";
3232
import DefaultDocument from "next/document";
3333
import { getAssetUrl } from "./lib/tools/getAssetUrl";
34+
import { setLangToUseIfProviderNotUsed } from "./lib/i18n";
3435
import "./dsfr/dsfr.css";
3536
import "./dsfr/utility/icons/icons.css";
36-
import { setLangToUseIfProviderNotUsed } from "./lib/i18n";
3737

3838
const fontUrlByFileBasename = {
3939
"Marianne-Light": marianneLightWoff2Url,

test/integration/cra/src/index.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import { Mui } from "./Mui";
66
import { useRoute, RouteProvider } from "./router";
77
import { Header } from "@codegouvfr/react-dsfr/Header";
88
import { fr } from "@codegouvfr/react-dsfr";
9+
import type { Link as TypeRouteLink } from "type-route";
10+
import { routes } from "./router";
11+
12+
declare module "@codegouvfr/react-dsfr" {
13+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
14+
export interface LinkProps extends TypeRouteLink { }
15+
16+
}
917

1018
startDsfrReact({
1119
"defaultColorScheme": "system"
@@ -20,19 +28,14 @@ createRoot(document.getElementById("root")!).render(
2028
nomDuSiteSlashService="Nom du site / service"
2129
links={[
2230
{
23-
"text": "Créer un espace",
24-
"iconId": "fr-icon-add-circle-line",
25-
"href": "#"
26-
},
27-
{
28-
"text": "Se connecter",
29-
"iconId": "fr-icon-lock-line",
30-
"href": "#"
31+
"text": "Home",
32+
"iconId": "fr-icon-home-4-fill",
33+
"linkProps": routes.home().link
3134
},
3235
{
33-
"text": "S'enregistrer",
34-
"iconId": "fr-icon-account-line",
35-
"href": "#"
36+
"text": "Mui playground",
37+
"iconId": "ri-play-circle-fill",
38+
"linkProps": routes.mui().link
3639
}
3740
]}
3841
/>

test/integration/next/pages/_app.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import { Header } from "@codegouvfr/react-dsfr/Header";
44
import { createEmotionSsrAdvancedApproach } from "tss-react/next";
55
import { useStyles } from "tss-react/dsfr";
66
import { fr } from "@codegouvfr/react-dsfr";
7+
import type { LinkProps as NextLinkProps } from "next/link";
78

9+
declare module "@codegouvfr/react-dsfr" {
10+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
11+
export interface LinkProps extends NextLinkProps { }
12+
13+
}
814

915
const {
1016
withDsfr,
@@ -46,17 +52,23 @@ function App({ Component, pageProps }: AppProps) {
4652
{
4753
"text": "Créer un espace",
4854
"iconId": "fr-icon-add-circle-line",
49-
"href": "#"
55+
"linkProps": {
56+
"href": "#"
57+
}
5058
},
5159
{
5260
"text": "Se connecter",
5361
"iconId": "fr-icon-lock-line",
54-
"href": "#"
62+
"linkProps": {
63+
"href": "#"
64+
}
5565
},
5666
{
5767
"text": "S'enregistrer",
5868
"iconId": "fr-icon-account-line",
59-
"href": "#"
69+
"linkProps": {
70+
"href": "#"
71+
}
6072
}
6173
]}
6274
/>

0 commit comments

Comments
 (0)