Skip to content

Commit 485728d

Browse files
committed
Feature Checkbox component
1 parent 76f33bc commit 485728d

File tree

8 files changed

+523
-173
lines changed

8 files changed

+523
-173
lines changed

COMPONENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- [ ] France Connect button
1111
- [x] Radio button
1212
- [ ] Radio rich
13-
- [ ] Checkbox
13+
- [x] Checkbox
1414
- [x] Cards
1515
- [x] Quote
1616
- [ ] Media

README.fr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ DSFR en pur JavaScript/CSS.
4545
- [x] la plupart des composants peuvent être rendus directement sur le serveur (voir [RSC](https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html)). Les autres sont étiquetées `"use client";`.
4646
- [x] [Intégration clef en main pour les différents frameworks de développement: vite, Next.js, y compris la version beta de Next 13 (configuration AppDir) et Create React App](https://react-dsfr.etalab.studio/) si votre
4747
framework n'est pas supporter, il suffit de demander notre, nous avons pour but de couvrir tous les cas d'usage effectifs.
48-
- [ ] tout [les composants de référence implémenter](https://www.systeme-de-design.gouv.fr/elements-d-interface). À ce jour 19/41, [see details](COMPONENTS.md)
48+
- [ ] tout [les composants de référence implémenter](https://www.systeme-de-design.gouv.fr/elements-d-interface). À ce jour 20/41, [see details](COMPONENTS.md)
4949
- [x] seulement le code des composants que vous utilisez effectivement sera inclus dans votre projet final.
5050
- [x] Intégration facultative avec [MUI](https://mui.com/). Si vous utilisez des composants MUI ils seront automatiquement adaptés pour ressembler à des composants DSFR.
5151
Voir là [documentation](https://react-dsfr.etalab.studio/mui-integration).

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ This module is a wrapper/compatibility layer for [@gouvfr/dsfr](https://github.c
4545
- [x] No [white flash when reloading in SSR setup](https://github.com/codegouvfr/@codegouvfr/react-dsfr/issues/2#issuecomment-1257263480).
4646
- [x] Most components are server component ready. The others are labeled with `"use client";`
4747
- [x] [Perfect integration with all major React framework: Next.js (PagesDir and AppDir), Create React App, Vue](https://react-dsfr.etalab.studio/).
48-
- [ ] All [the components](https://www.systeme-de-design.gouv.fr/elements-d-interface) are implemented (19/41, [see details](COMPONENTS.md))
48+
- [ ] All [the components](https://www.systeme-de-design.gouv.fr/elements-d-interface) are implemented (20/41, [see details](COMPONENTS.md))
4949
- [x] Three shakable distribution, cherry pick the components you import. (It's not all in a big .js bundle)
5050
- [x] Optional integration with [MUI](https://mui.com/). If you use MUI components they will
5151
be automatically adapted to look like [DSFR components](https://www.systeme-de-design.gouv.fr/elements-d-interface). See [documentation](https://react-dsfr.etalab.studio/mui-integration).

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
"./Header": "./dist/Header.js",
150150
"./Footer": "./dist/Footer.js",
151151
"./Display": "./dist/Display.js",
152+
"./Checkbox": "./dist/Checkbox.js",
152153
"./Card": "./dist/Card.js",
153154
"./CallOut": "./dist/CallOut.js",
154155
"./ButtonsGroup": "./dist/ButtonsGroup.js",

src/Checkbox.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React, { memo, forwardRef } from "react";
2+
import { symToStr } from "tsafe/symToStr";
3+
import { Fieldset, type FieldsetProps } from "./shared/Fieldset";
4+
5+
export type CheckboxProps = FieldsetProps.Common;
6+
7+
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-checkbox> */
8+
export const Checkbox = memo(
9+
forwardRef<HTMLFieldSetElement, CheckboxProps>((props, ref) => (
10+
<Fieldset ref={ref} type="checkbox" {...props} />
11+
))
12+
);
13+
14+
Checkbox.displayName = symToStr({ Checkbox });
15+
16+
export default Checkbox;

src/RadioButtons.tsx

Lines changed: 6 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,178 +1,14 @@
1-
import React, {
2-
useId,
3-
memo,
4-
forwardRef,
5-
type ReactNode,
6-
type CSSProperties,
7-
type InputHTMLAttributes,
8-
type DetailedHTMLProps
9-
} from "react";
1+
import React, { memo, forwardRef } from "react";
102
import { symToStr } from "tsafe/symToStr";
11-
import { assert } from "tsafe/assert";
12-
import type { Equals } from "tsafe";
13-
import { cx } from "./tools/cx";
14-
import { fr } from "./fr";
3+
import { Fieldset, type FieldsetProps } from "./shared/Fieldset";
154

16-
export type RadioButtonsProps = {
17-
className?: string;
18-
classes?: Partial<Record<"root" | "legend" | "content", string>>;
19-
style?: CSSProperties;
20-
legend: ReactNode;
21-
hintText?: ReactNode;
22-
name?: string;
23-
options: {
24-
label: ReactNode;
25-
hintText?: ReactNode;
26-
nativeInputProps: DetailedHTMLProps<
27-
InputHTMLAttributes<HTMLInputElement>,
28-
HTMLInputElement
29-
>;
30-
}[];
31-
/** Default: "vertical" */
32-
orientation?: "vertical" | "horizontal";
33-
/** Default: "default" */
34-
state?: "success" | "error" | "default";
35-
/**
36-
* The message won't be displayed if state is "default".
37-
* If the state is "error" providing a message is mandatory
38-
**/
39-
stateRelatedMessage?: ReactNode;
40-
/** Default: false */
41-
disabled?: boolean;
42-
};
5+
export type RadioButtonsProps = FieldsetProps.Common & { name?: string };
436

447
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-radiobutton> */
458
export const RadioButtons = memo(
46-
forwardRef<HTMLFieldSetElement, RadioButtonsProps>((props, ref) => {
47-
const {
48-
className,
49-
classes = {},
50-
style,
51-
name: name_props,
52-
legend,
53-
hintText,
54-
options,
55-
orientation = "vertical",
56-
state = "default",
57-
stateRelatedMessage,
58-
disabled = false,
59-
...rest
60-
} = props;
61-
62-
assert<Equals<keyof typeof rest, never>>();
63-
64-
const { getRadioId, legendId, errorDescId, successDescId } = (function useClosure() {
65-
const id = `radio${name_props === undefined ? "" : `-${name_props}`}-${useId()}`;
66-
67-
const getRadioId = (i: number) => `${id}-${i}`;
68-
69-
const legendId = `${id}-legend`;
70-
71-
const errorDescId = `${id}-desc-error`;
72-
const successDescId = `${id}-desc-valid`;
73-
74-
return { getRadioId, legendId, errorDescId, successDescId };
75-
})();
76-
77-
const name = (function useClosure() {
78-
const id = useId();
79-
80-
return name_props ?? `radio-name-${id}`;
81-
})();
82-
83-
return (
84-
<fieldset
85-
className={cx(
86-
fr.cx(
87-
"fr-fieldset",
88-
orientation === "horizontal" && "fr-fieldset--inline",
89-
(() => {
90-
switch (state) {
91-
case "default":
92-
return undefined;
93-
case "error":
94-
return "fr-fieldset--error";
95-
case "success":
96-
return "fr-fieldset--valid";
97-
}
98-
})()
99-
),
100-
classes.root,
101-
className
102-
)}
103-
disabled={disabled}
104-
style={style}
105-
aria-labelledby={cx(
106-
legendId,
107-
(() => {
108-
switch (state) {
109-
case "default":
110-
return undefined;
111-
case "error":
112-
return errorDescId;
113-
case "success":
114-
return successDescId;
115-
}
116-
})()
117-
)}
118-
role={state === "default" ? undefined : "group"}
119-
{...rest}
120-
ref={ref}
121-
>
122-
<legend
123-
id={legendId}
124-
className={cx(fr.cx("fr-fieldset__legend", "fr-text--regular"), classes.legend)}
125-
>
126-
{legend}
127-
{hintText !== undefined && (
128-
<span className={fr.cx("fr-hint-text")}>{hintText}</span>
129-
)}
130-
</legend>
131-
<div className={cx(fr.cx("fr-fieldset__content"), classes.content)}>
132-
{options.map(({ label, hintText, nativeInputProps }, i) => (
133-
<div className={fr.cx("fr-radio-group")} key={i}>
134-
<input
135-
type="radio"
136-
id={getRadioId(i)}
137-
name={name}
138-
{...nativeInputProps}
139-
/>
140-
<label className={fr.cx("fr-label")} htmlFor={getRadioId(i)}>
141-
{label}
142-
{hintText !== undefined && (
143-
<span className={fr.cx("fr-hint-text")}>{hintText}</span>
144-
)}
145-
</label>
146-
</div>
147-
))}
148-
</div>
149-
{state !== "default" && (
150-
<p
151-
id={(() => {
152-
switch (state) {
153-
case "error":
154-
return errorDescId;
155-
case "success":
156-
return successDescId;
157-
}
158-
})()}
159-
className={fr.cx(
160-
(() => {
161-
switch (state) {
162-
case "error":
163-
return "fr-error-text";
164-
case "success":
165-
return "fr-valid-text";
166-
}
167-
})()
168-
)}
169-
>
170-
{stateRelatedMessage ?? ""}
171-
</p>
172-
)}
173-
</fieldset>
174-
);
175-
})
9+
forwardRef<HTMLFieldSetElement, RadioButtonsProps>((props, ref) => (
10+
<Fieldset ref={ref} type="radio" {...props} />
11+
))
17612
);
17713

17814
RadioButtons.displayName = symToStr({ RadioButtons });

0 commit comments

Comments
 (0)