Skip to content

Commit 485b75a

Browse files
committed
Feat: Input component
1 parent ffb6737 commit 485b75a

File tree

5 files changed

+84
-31
lines changed

5 files changed

+84
-31
lines changed

COMPONENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@
4141
- [ ] Download
4242
- [ ] Transcription
4343
- [x] Tile
44+
- [x] Input

README.fr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ DSFR en pur JavaScript/CSS.
4848
- [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";`.
4949
- [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
5050
framework n'est pas supporter, il suffit de demander notre, nous avons pour but de couvrir tous les cas d'usage effectifs.
51-
- [ ] tout [les composants de référence implémenter](https://www.systeme-de-design.gouv.fr/elements-d-interface). À ce jour 14/42, [see details](COMPONENTS.md)
51+
- [ ] tout [les composants de référence implémenter](https://www.systeme-de-design.gouv.fr/elements-d-interface). À ce jour 15/42, [see details](COMPONENTS.md)
5252
- [x] seulement le code des composants que vous utilisez effectivement sera inclus dans votre projet final.
5353
- [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.
5454
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 (14/42, [see details](COMPONENTS.md))
48+
- [ ] All [the components](https://www.systeme-de-design.gouv.fr/elements-d-interface) are implemented (15/42, [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).

src/Input.tsx

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ export namespace InputProps {
2727
};
2828

2929
export type WithoutSpecialState = {
30-
state?: never;
30+
state?: "default";
3131
stateRelatedMessage?: never;
3232
};
3333

3434
export type WithoutTextArea = {
3535
/** Default: false */
36-
isTextArea?: false;
36+
textArea?: false;
3737
/** Props forwarded to the underlying <input /> element */
3838
nativeInputProps?: InputHTMLAttributes<HTMLInputElement>;
3939

@@ -42,7 +42,7 @@ export namespace InputProps {
4242

4343
export type WithTextArea = {
4444
/** Default: false */
45-
isTextArea: true;
45+
textArea: true;
4646
/** Props forwarded to the underlying <textarea /> element */
4747
nativeTextAreaProps?: TextareaHTMLAttributes<HTMLTextAreaElement>;
4848

@@ -62,18 +62,18 @@ export const Input = memo(
6262
disabled = false,
6363
iconId: iconId_props,
6464
classes = {},
65-
state,
65+
state = "default",
6666
stateRelatedMessage,
67-
isTextArea = false,
67+
textArea = false,
6868
nativeTextAreaProps,
6969
nativeInputProps,
7070
...rest
7171
} = props;
7272

7373
const nativeInputOrTextAreaProps =
74-
(isTextArea ? nativeTextAreaProps : nativeInputProps) ?? {};
74+
(textArea ? nativeTextAreaProps : nativeInputProps) ?? {};
7575

76-
const NativeInputOrTexArea = isTextArea ? "textarea" : "input";
76+
const NativeInputOrTexArea = textArea ? "textarea" : "input";
7777

7878
assert<Equals<keyof typeof rest, never>>();
7979

@@ -91,16 +91,17 @@ export const Input = memo(
9191
fr.cx(
9292
"fr-input-group",
9393
disabled && "fr-input-group--disabled",
94-
state !== undefined &&
95-
(() => {
96-
switch (state) {
97-
case "error":
98-
return "fr-input-group--error";
99-
case "success":
100-
return "fr-input-group--valid";
101-
}
102-
assert<Equals<typeof state, never>>(false);
103-
})()
94+
(() => {
95+
switch (state) {
96+
case "error":
97+
return "fr-input-group--error";
98+
case "success":
99+
return "fr-input-group--valid";
100+
case "default":
101+
return undefined;
102+
}
103+
assert<Equals<typeof state, never>>(false);
104+
})()
104105
),
105106
classes.root,
106107
className
@@ -119,16 +120,17 @@ export const Input = memo(
119120
className={cx(
120121
fr.cx(
121122
"fr-input",
122-
state !== undefined &&
123-
(() => {
124-
switch (state) {
125-
case "error":
126-
return "fr-input--error";
127-
case "success":
128-
return "fr-input--valid";
129-
}
130-
assert<Equals<typeof state, never>>(false);
131-
})()
123+
(() => {
124+
switch (state) {
125+
case "error":
126+
return "fr-input--error";
127+
case "success":
128+
return "fr-input--valid";
129+
case "default":
130+
return undefined;
131+
}
132+
assert<Equals<typeof state, never>>(false);
133+
})()
132134
),
133135
classes.nativeInputOrTextArea
134136
)}
@@ -151,7 +153,7 @@ export const Input = memo(
151153
</div>
152154
);
153155
})()}
154-
{stateRelatedMessage !== undefined && (
156+
{state !== "default" && (
155157
<p
156158
id={messageId}
157159
className={cx(

stories/Input.stories.tsx

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,69 @@
11
import { Input } from "../dist/Input";
2+
import type { InputProps } from "../dist/Input";
23
import { sectionName } from "./sectionName";
34
import { getStoryFactory } from "./getStory";
5+
import { assert } from "tsafe/assert";
6+
import type { Equals } from "tsafe";
47

58
const { meta, getStory } = getStoryFactory({
69
sectionName,
710
"wrappedComponent": { Input },
811
"description": `
912
- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/champ-de-saisie)
1013
- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Input.tsx)`,
14+
"argTypes": {
15+
"disabled": {
16+
"control": { "type": "boolean" }
17+
},
18+
"iconId": {
19+
"options": (() => {
20+
const options = ["fr-icon-checkbox-circle-line", "ri-ancient-gate-fill"] as const;
21+
22+
assert<
23+
typeof options[number] extends NonNullable<InputProps["iconId"]> ? true : false
24+
>();
25+
26+
return options;
27+
})(),
28+
"control": { "type": "radio" }
29+
},
30+
"state": {
31+
"options": (() => {
32+
const options = ["default", "success", "error"] as const;
33+
34+
assert<Equals<typeof options[number] | undefined, InputProps["state"]>>();
35+
36+
return options;
37+
})(),
38+
"control": { "type": "radio" }
39+
},
40+
"textArea": {
41+
"control": { "type": "boolean" }
42+
},
43+
"nativeInputProps": {
44+
"description": `An object that is forwarded as props to te underlying native \`<input />\` element.
45+
This is where you pass the \`name\` prop or \`onKeyDown\` for example.`,
46+
"control": { "type": null }
47+
},
48+
"nativeTextAreaProps": {
49+
"description": `Like the \`nativeInputProps\` but when \`textArea\` is \`true\``,
50+
"control": { "type": null }
51+
}
52+
},
1153
"disabledProps": ["lang"]
1254
});
1355

1456
export default meta;
1557

1658
export const Default = getStory({
59+
"label": "Label champ de saisie",
60+
"hintText": "Texte de description",
61+
"state": "default",
62+
"stateRelatedMessage": "Text de validation / d'explication de l'erreur" as unknown as undefined,
63+
"textArea": false
64+
});
65+
66+
export const Basic = getStory({
1767
"label": "Label champ de saisie"
1868
});
1969

@@ -41,7 +91,7 @@ export const WithHint = getStory({
4191

4292
export const TextArea = getStory({
4393
"label": "Label champs de saisie",
44-
"isTextArea": true
94+
"textArea": true
4595
});
4696

4797
export const WithIcon = getStory({

0 commit comments

Comments
 (0)