Skip to content

Commit f961d94

Browse files
authored
Merge pull request #19 from codegouvfr/main
Add SkipLinks component
2 parents 6eedf2c + 86ea7a5 commit f961d94

File tree

20 files changed

+1067
-135
lines changed

20 files changed

+1067
-135
lines changed

CONTRIBUTING.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Hello friends 👋,
1+
`Hello friends 👋,
22

33
Would you help us implement [the components](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/accordeon)?
44
Thank you so much to anyone that would!
@@ -23,6 +23,17 @@ A few things:
2323
- 🌎 Avoid hard coding text in JSX, use [the i18n mechanism](https://react-dsfr.etalab.studio/i18n) instead. [Here is an example](https://github.com/codegouvfr/react-dsfr/blob/bbaf4a81d78de08d6fdcb059a9f4cb8a78ce4d5a/src/DarkModeSwitch.tsx#L162-L199). (Don't worry about providing translations other than French.)
2424
- 🍳 If you have to arbitrate between ease of use and customisability I'd encourage you to favor ease of use. People that would need a greater level of customizability can always fall back to making their own wrapper from the reference documentation using [`fr.cx()`](https://react-dsfr.etalab.studio/cx).
2525

26+
## Getting TypeScript error in VSCode but the console says everything's right?
27+
28+
Because of how this project is setup TypeScript unaware that files have changed.
29+
You don't need to restart VSCode, just restart the TypeScript server.
30+
31+
https://user-images.githubusercontent.com/6702424/206942271-0dc9b94a-1c2b-4073-99d7-96f7fe862bc4.mov
32+
33+
Assets imports error in Storybook can be solved by opening the `stories/global.d.ts` file:
34+
35+
https://user-images.githubusercontent.com/6702424/206940923-8d2d1113-8b81-4f61-8c4e-66101c9fe67e.mov
36+
2637
Thank You Very Much ❤️
2738

2839
PS: If you want to contribute to the Doc website. You can edit [the source Markdown](https://github.com/codegouvfr/react-dsfr/tree/v1_docs) or ask me for access to our GitBook. (We'll migrate to Docusaurus once we have the DSFR theme for it ready.)

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@codegouvfr/react-dsfr",
3-
"version": "0.0.88",
3+
"version": "0.0.94",
44
"description": "French State Design System React integration library",
55
"repository": {
66
"type": "git",
@@ -72,9 +72,8 @@
7272
"devDependencies": {
7373
"@babel/core": "^7.20.2",
7474
"@emotion/react": "^11.10.4",
75-
"tss-react": "^4.4.4",
7675
"@emotion/styled": "^11.10.4",
77-
"@gouvfr/dsfr": "1.8.5",
76+
"@gouvfr/dsfr": "1.8.4",
7877
"@mui/material": "^5.10.7",
7978
"@storybook/addon-actions": "^6.5.13",
8079
"@storybook/addon-essentials": "^6.5.13",
@@ -112,6 +111,7 @@
112111
"remixicon": "^2.5.0",
113112
"storybook-dark-mode": "^1.1.2",
114113
"ts-node": "^10.9.1",
114+
"tss-react": "^4.4.4",
115115
"typescript": "^4.9.1-beta",
116116
"vitest": "^0.24.3"
117117
},
@@ -128,7 +128,7 @@
128128
"./Notice": "./dist/Notice.js",
129129
"./Highlight": "./dist/Highlight.js",
130130
"./Header": "./dist/Header/index.js",
131-
"./DisplaySettingsDialog": "./dist/DisplaySettingsDialog.js",
131+
"./Display": "./dist/Display.js",
132132
"./Badge": "./dist/Badge.js",
133133
"./Alert": "./dist/Alert.js",
134134
"./Accordion": "./dist/Accordion.js"

src/DisplaySettingsDialog.tsx renamed to src/Display.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import ArtworkSystemSvg from "./dsfr/artwork/system.svg";
1111
import { getAssetUrl } from "./lib/tools/getAssetUrl";
1212
import type { HeaderProps } from "./Header";
1313

14-
export type DisplaySettingsDialog = {
14+
export type Display = {
1515
className?: string;
1616
};
1717

1818
const dialogId = "fr-theme-modal";
1919
const dialogTitleId = "fr-theme-modal-title";
2020

21-
export const headerQuickAccessDisplaySettingsItem: HeaderProps.QuickAccessItem.Button = {
21+
export const headerQuickAccessDisplay: HeaderProps.QuickAccessItem.Button = {
2222
"buttonProps": {
2323
"aria-controls": dialogId,
2424
...({ "data-fr-opened": false } as {})
@@ -34,9 +34,9 @@ export const headerQuickAccessDisplaySettingsItem: HeaderProps.QuickAccessItem.B
3434
})()
3535
};
3636

37-
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-darkmodeswitch> */
38-
export const DisplaySettingsDialog = memo(
39-
forwardRef<HTMLDialogElement, DisplaySettingsDialog>((props, ref) => {
37+
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-display> */
38+
export const Display = memo(
39+
forwardRef<HTMLDialogElement, Display>((props, ref) => {
4040
const { className, ...rest } = props;
4141

4242
assert<Equals<keyof typeof rest, never>>();
@@ -101,9 +101,9 @@ export const DisplaySettingsDialog = memo(
101101
})
102102
);
103103

104-
DisplaySettingsDialog.displayName = symToStr({ DisplaySettingsDialog });
104+
Display.displayName = symToStr({ Display });
105105

106-
export default DisplaySettingsDialog;
106+
export default Display;
107107

108108
const RadioGroup = memo((props: { theme: "dark" | "light" | "system" }) => {
109109
const { theme } = props;
@@ -159,8 +159,8 @@ const RadioGroup = memo((props: { theme: "dark" | "light" | "system" }) => {
159159

160160
RadioGroup.displayName = symToStr({ RadioGroup });
161161

162-
const { useTranslation, addDisplaySettingsDialogTranslations } = createComponentI18nApi({
163-
"componentName": symToStr({ DisplaySettingsDialog }),
162+
const { useTranslation, addDisplayTranslations } = createComponentI18nApi({
163+
"componentName": symToStr({ Display }),
164164
"frMessages": {
165165
/* spell-checker: disable */
166166
"display settings": "Paramètres d'affichage",
@@ -174,7 +174,7 @@ const { useTranslation, addDisplaySettingsDialogTranslations } = createComponent
174174
}
175175
});
176176

177-
addDisplaySettingsDialogTranslations({
177+
addDisplayTranslations({
178178
"lang": "en",
179179
"messages": {
180180
"display settings": "Display settings",
@@ -187,7 +187,7 @@ addDisplaySettingsDialogTranslations({
187187
}
188188
});
189189

190-
addDisplaySettingsDialogTranslations({
190+
addDisplayTranslations({
191191
"lang": "es",
192192
"messages": {
193193
/* spell-checker: disable */
@@ -198,4 +198,4 @@ addDisplaySettingsDialogTranslations({
198198
}
199199
});
200200

201-
export { addDisplaySettingsDialogTranslations };
201+
export { addDisplayTranslations };

src/Header/Header.tsx

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { fr } from "../lib";
44
import { createComponentI18nApi } from "../lib/i18n";
55
import { symToStr } from "tsafe/symToStr";
66
import { cx } from "../lib/tools/cx";
7-
import type { LinkProps } from "../lib/routing";
7+
import type { RegisteredLinkProps } from "../lib/routing";
88
import { useLink } from "../lib/routing";
99
import type { MainNavigationProps } from "./MainNavigation";
1010
import { MainNavigation } from "./MainNavigation";
@@ -17,11 +17,21 @@ export type HeaderProps = {
1717
brandTop: ReactNode;
1818
serviceTitle?: ReactNode;
1919
serviceTagline?: ReactNode;
20-
/** Don't forget the title on the link for accessibility*/
21-
homeLinkProps: LinkProps;
20+
homeLinkProps: RegisteredLinkProps & { title: string };
2221
navItems?: MainNavigationProps.Item[];
2322
/** There should be at most three of them */
2423
quickAccessItems?: HeaderProps.QuickAccessItem[];
24+
operatorLogo?: {
25+
orientation: "horizontal" | "vertical";
26+
/**
27+
* Expected ratio:
28+
* If "vertical": 9x16
29+
* If "horizontal": 16x9
30+
*/
31+
imgUrl: string;
32+
/** Textual alternative of the image, it MUST include the text present in the image*/
33+
alt: string;
34+
};
2535
renderSearchInput?: (
2636
/**
2737
* id and name must be forwarded to the <input /> component
@@ -49,11 +59,13 @@ export type HeaderProps = {
4959
| "serviceTagline"
5060
| "menu"
5161
| "menuLinks"
52-
| "navRoot"
62+
| "nav"
5363
| "navList"
5464
| "navItem"
5565
| "navLink"
56-
| "navBtn",
66+
| "navBtn"
67+
| "navMenu"
68+
| "navMenuList",
5769
string
5870
>
5971
>;
@@ -69,7 +81,7 @@ export namespace HeaderProps {
6981
};
7082

7183
export type Link = Common & {
72-
linkProps: LinkProps;
84+
linkProps: RegisteredLinkProps;
7385
buttonProps?: undefined;
7486
};
7587

@@ -94,6 +106,7 @@ export const Header = memo(
94106
homeLinkProps,
95107
navItems = [],
96108
quickAccessItems = [],
109+
operatorLogo,
97110
renderSearchInput,
98111
classes = {},
99112
...rest
@@ -142,6 +155,26 @@ export const Header = memo(
142155
);
143156
})()}
144157
</div>
158+
{operatorLogo !== undefined && (
159+
<div className={fr.cx("fr-header__operator")}>
160+
<Link {...homeLinkProps}>
161+
<img
162+
className={fr.cx("fr-responsive-img")}
163+
style={(() => {
164+
switch (operatorLogo.orientation) {
165+
case "vertical":
166+
return { "width": "3.5rem" };
167+
case "horizontal":
168+
return { "maxWidth": "9.0625rem" };
169+
}
170+
})()}
171+
src={operatorLogo.imgUrl}
172+
alt={operatorLogo.alt}
173+
/>
174+
</Link>
175+
</div>
176+
)}
177+
145178
{(quickAccessItems.length > 0 ||
146179
renderSearchInput !== undefined) && (
147180
<div
@@ -311,11 +344,13 @@ export const Header = memo(
311344
<MainNavigation
312345
items={navItems}
313346
classes={{
314-
"root": classes.navRoot,
347+
"root": classes.nav,
315348
"list": classes.navList,
316349
"item": classes.navItem,
317350
"link": classes.navLink,
318-
"btn": classes.navBtn
351+
"btn": classes.navBtn,
352+
"menu": classes.navMenu,
353+
"menuList": classes.navMenuList
319354
}}
320355
/>
321356
)}

src/Header/MainNavigation/MainNavigation.tsx

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createComponentI18nApi } from "../../lib/i18n";
44
import { symToStr } from "tsafe/symToStr";
55
import { assert } from "tsafe/assert";
66
import type { Equals } from "tsafe";
7-
import type { LinkProps } from "../../lib/routing";
7+
import type { RegisteredLinkProps } from "../../lib/routing";
88
import { fr } from "../../lib";
99
import { cx } from "../../lib/tools/cx";
1010
import { useLink } from "../../lib/routing";
@@ -16,7 +16,22 @@ import { MegaMenu } from "./MegaMenu";
1616
export type MainNavigationProps = {
1717
className?: string;
1818
items: MainNavigationProps.Item[];
19-
classes?: Partial<Record<"root" | "list" | "item" | "link" | "btn", string>>;
19+
classes?: Partial<
20+
Record<
21+
| "root"
22+
| "list"
23+
| "item"
24+
| "link"
25+
| "btn"
26+
| "menu"
27+
| "menuList"
28+
| "megaMenu"
29+
| "megaMenuLeader"
30+
| "megaMenuCategory"
31+
| "megaMenuList",
32+
string
33+
>
34+
>;
2035
};
2136

2237
export namespace MainNavigationProps {
@@ -30,21 +45,24 @@ export namespace MainNavigationProps {
3045
};
3146

3247
export type Link = Common & {
33-
linkProps: LinkProps;
34-
menuProps?: undefined;
35-
megaMenuProps?: undefined;
48+
linkProps: RegisteredLinkProps;
49+
menuLinks?: undefined;
50+
megaMenu?: undefined;
3651
};
3752

3853
export type Menu = Common & {
3954
linkProps?: undefined;
40-
menuProps: MenuProps;
41-
megaMenuProps?: undefined;
55+
menuLinks: MenuProps.Link[];
56+
megaMenu?: undefined;
4257
};
4358

4459
export type MegaMenu = Common & {
4560
linkProps?: undefined;
46-
menuProps?: undefined;
47-
megaMenuProps: MegaMenuProps;
61+
menuLinks?: undefined;
62+
megaMenu: {
63+
leader?: MegaMenuProps.Leader;
64+
categories: MegaMenuProps.Category[];
65+
};
4866
};
4967
}
5068
}
@@ -83,8 +101,8 @@ export const MainNavigation = memo(
83101
text,
84102
isActive = false,
85103
linkProps,
86-
menuProps,
87-
megaMenuProps
104+
menuLinks = [],
105+
megaMenu
88106
},
89107
i
90108
) => (
@@ -114,24 +132,30 @@ export const MainNavigation = memo(
114132
>
115133
{text}
116134
</button>
117-
{menuProps !== undefined && (
135+
{menuLinks.length !== 0 && (
118136
<Menu
119-
{...menuProps}
120-
className={cx(
121-
fr.cx("fr-collapse"),
122-
menuProps.className
123-
)}
137+
classes={{
138+
"root": cx(fr.cx("fr-collapse"), classes.root),
139+
"list": classes.menuList
140+
}}
141+
links={menuLinks}
124142
id={getMenuId(i)}
125143
/>
126144
)}
127-
{megaMenuProps !== undefined && (
145+
{megaMenu !== undefined && (
128146
<MegaMenu
129-
{...megaMenuProps}
130-
className={cx(
131-
fr.cx("fr-collapse"),
132-
megaMenuProps.className
133-
)}
147+
classes={{
148+
"root": cx(
149+
fr.cx("fr-collapse"),
150+
classes.megaMenu
151+
),
152+
"leader": classes.megaMenuLeader,
153+
"category": classes.megaMenuCategory,
154+
"list": classes.menuList
155+
}}
134156
id={getMenuId(i)}
157+
leader={megaMenu.leader}
158+
categories={megaMenu.categories}
135159
/>
136160
)}
137161
</>

0 commit comments

Comments
 (0)