From 0d64119ee4b261d4867e1035a7f1b168f5ad961c Mon Sep 17 00:00:00 2001 From: Enguerran Weiss Date: Wed, 13 Dec 2023 17:43:00 +0100 Subject: [PATCH 1/4] Fieldset handles RadioRichButtons --- src/Checkbox.tsx | 4 +- src/RadioButtons.tsx | 4 +- src/RadioRichButtons.tsx | 16 ++++++ src/shared/Fieldset.tsx | 45 ++++++++++++---- test/types/Checkbox.tsx | 48 +++++++++++++++++ test/types/RadioButtons.tsx | 101 ++++++++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 src/RadioRichButtons.tsx create mode 100644 test/types/Checkbox.tsx create mode 100644 test/types/RadioButtons.tsx diff --git a/src/Checkbox.tsx b/src/Checkbox.tsx index c865599cd..dfa9daec8 100644 --- a/src/Checkbox.tsx +++ b/src/Checkbox.tsx @@ -2,12 +2,12 @@ import React, { memo, forwardRef } from "react"; import { symToStr } from "tsafe/symToStr"; import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; -export type CheckboxProps = FieldsetProps.Common; +export type CheckboxProps = Omit; /** @see */ export const Checkbox = memo( forwardRef((props, ref) => ( -
+
)) ); diff --git a/src/RadioButtons.tsx b/src/RadioButtons.tsx index a692e0764..ce36f581a 100644 --- a/src/RadioButtons.tsx +++ b/src/RadioButtons.tsx @@ -2,12 +2,12 @@ import React, { memo, forwardRef } from "react"; import { symToStr } from "tsafe/symToStr"; import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; -export type RadioButtonsProps = FieldsetProps.Common & { name?: string }; +export type RadioButtonsProps = Omit; /** @see */ export const RadioButtons = memo( forwardRef((props, ref) => ( -
+
)) ); diff --git a/src/RadioRichButtons.tsx b/src/RadioRichButtons.tsx new file mode 100644 index 000000000..176a0ba83 --- /dev/null +++ b/src/RadioRichButtons.tsx @@ -0,0 +1,16 @@ +import React, { memo, forwardRef } from "react"; +import { symToStr } from "tsafe/symToStr"; +import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; + +export type RadioRichButtonsProps = Omit; + +/** @see */ +export const RadioRichButtons = memo( + forwardRef((props, ref) => ( +
+ )) +); + +RadioRichButtons.displayName = symToStr({ RadioRichButtons }); + +export default RadioRichButtons; diff --git a/src/shared/Fieldset.tsx b/src/shared/Fieldset.tsx index 101730528..9ebdc1678 100644 --- a/src/shared/Fieldset.tsx +++ b/src/shared/Fieldset.tsx @@ -14,9 +14,20 @@ import { cx } from "../tools/cx"; import { fr } from "../fr"; import { useAnalyticsId } from "../tools/useAnalyticsId"; -export type FieldsetProps = FieldsetProps.Radio | FieldsetProps.Checkbox; +export type FieldsetProps = FieldsetProps.Radio | FieldsetProps.Checkbox | FieldsetProps.RadioRich; export namespace FieldsetProps { + export type RegularOption = { + label: ReactNode; + hintText?: ReactNode; + nativeInputProps: DetailedHTMLProps< + InputHTMLAttributes, + HTMLInputElement + >; + }; + export type RadioRichOption = RegularOption & { + illustration: ReactNode; + }; export type Common = { className?: string; id?: string; @@ -24,14 +35,7 @@ export namespace FieldsetProps { style?: CSSProperties; legend?: ReactNode; hintText?: ReactNode; - options: { - label: ReactNode; - hintText?: ReactNode; - nativeInputProps: DetailedHTMLProps< - InputHTMLAttributes, - HTMLInputElement - >; - }[]; + /** Default: "vertical" */ orientation?: "vertical" | "horizontal"; /** Default: "default" */ @@ -50,11 +54,22 @@ export namespace FieldsetProps { export type Radio = Common & { type: "radio"; name?: string; + rich?: false; + options: RegularOption[]; }; export type Checkbox = Common & { type: "checkbox"; name?: never; + rich?: false; + options: RegularOption[]; + }; + + export type RadioRich = Common & { + type: "radio"; + name?: string; + rich: true; + options: RadioRichOption[]; }; } @@ -76,6 +91,7 @@ export const Fieldset = memo( type, name: name_props, small = false, + rich, ...rest } = props; @@ -147,7 +163,11 @@ export const Fieldset = memo(
{options.map(({ label, hintText, nativeInputProps }, i) => (
{hintText} )} + {rich && ( +
+ {options[i].illustration} +
+ )}
))}
diff --git a/test/types/Checkbox.tsx b/test/types/Checkbox.tsx new file mode 100644 index 000000000..3e3cc56b3 --- /dev/null +++ b/test/types/Checkbox.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { Checkbox } from "../../src/Checkbox"; + +{ + ; +} +{ + + } + ]} + />; +} diff --git a/test/types/RadioButtons.tsx b/test/types/RadioButtons.tsx new file mode 100644 index 000000000..ed033c385 --- /dev/null +++ b/test/types/RadioButtons.tsx @@ -0,0 +1,101 @@ +import React from "react"; +import { RadioButtons } from "../../src/RadioButtons"; +import { RadioRichButtons } from "../../src/RadioRichButtons"; + +{ + ; +} + +{ + + }, + { + "label": "Label 2", + "hintText": "Hint text", + "nativeInputProps": { + "value": 2 + }, + "illustration": illustration + } + ]} + />; +} + +{ + + }, + { + "label": "Label 2", + "hintText": "Hint text", + "nativeInputProps": { + "value": 2 + } + } + ]} + />; +} + +{ + + }, + // @ts-expect-error + { + "label": "Label 2", + "hintText": "Hint text", + "nativeInputProps": { + "value": 2 + } + } + ]} + />; +} From ee455d87e50a2b95a1e6ab9f0c2cc9300ae19b59 Mon Sep 17 00:00:00 2001 From: Enguerran Weiss Date: Thu, 14 Dec 2023 13:47:15 +0100 Subject: [PATCH 2/4] added RadioRichButtons story --- src/RadioRichButtons.tsx | 2 +- stories/RadioRichButtons.stories.tsx | 424 +++++++++++++++++++++++++++ 2 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 stories/RadioRichButtons.stories.tsx diff --git a/src/RadioRichButtons.tsx b/src/RadioRichButtons.tsx index 176a0ba83..1f20093c3 100644 --- a/src/RadioRichButtons.tsx +++ b/src/RadioRichButtons.tsx @@ -4,7 +4,7 @@ import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; export type RadioRichButtonsProps = Omit; -/** @see */ +/** @see */ export const RadioRichButtons = memo( forwardRef((props, ref) => (
diff --git a/stories/RadioRichButtons.stories.tsx b/stories/RadioRichButtons.stories.tsx new file mode 100644 index 000000000..9de928a38 --- /dev/null +++ b/stories/RadioRichButtons.stories.tsx @@ -0,0 +1,424 @@ +import React from "react"; +import { RadioRichButtons, type RadioRichButtonsProps } from "../dist/RadioRichButtons"; +import { sectionName } from "./sectionName"; +import { getStoryFactory } from "./getStory"; +import { assert } from "tsafe/assert"; +import type { Equals } from "tsafe"; + +const { meta, getStory } = getStoryFactory({ + sectionName, + "wrappedComponent": { RadioRichButtons }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-radio-riche) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/RadioRichButtons.tsx) + +## Controlled + +\`\`\`tsx +import { useState } from "react"; +import { RadioRichButtons } from "@codegouvfr/react-dsfr/RadioRichButtons"; + +function MyComponent(){ + + const [ value, setValue ] = useState<"one" | "two" | "three" | undefined>(undefined); + + return ( + setValue("one") + }, + illustration: illustration + }, + { + label: "Label radio 2", + nativeInputProps: { + checked: value === "two" + onChange: ()=> setValue("two") + }, + illustration: illustration + }, + { + label: "Label radio 3", + nativeInputProps: { + checked: value === "three" + onChange: ()=> setValue("three") + }, + illustration: illustration + } + ]} + /> + ); + +} +\`\`\` + +## Uncontrolled + +\`\`\`tsx +import { useState } from "react"; +import { RadioRichButtons } from "@codegouvfr/react-dsfr/RadioRichButtons"; + +function MyComponent(){ + + return ( +
+ + }, + { + label: "Label radio 2", + nativeInputProps: { + value: "two" + }, + illustration: illustration + }, + { + label: "Label radio 3", + nativeInputProps: { + value: "three" + }, + illustration: illustration + } + ]} + /> + + ); + +} +\`\`\` + + +`, + "argTypes": { + "name": { + "description": + "The name that will be applied to all the underlying ``", + "control": { "type": "text" } + }, + "options": { + "description": `An array describing the radio options. + \`nativeInputProps\` is an object that you would pass as prop to \`\`, + this is where you define the value for each option. + \`illustration\` is an mandatory React element that will be displayed on the right of the label. + `, + "control": { "type": "null" } + }, + "orientation": { + "description": "Default: 'vertical'", + "options": (() => { + const options = ["horizontal", "vertical"] as const; + + assert< + Equals + >(); + + return options; + })(), + "control": { "type": "radio" } + }, + "state": { + "description": "Default: 'default'", + "options": (() => { + const options = ["success", "error", "default"] as const; + + assert< + Equals + >(); + + return options; + })(), + "control": { "type": "radio" } + }, + "stateRelatedMessage": { + "description": `The message won't be displayed if state is "default". + If the state is "error" providing a message is mandatory`, + "control": { "type": "text" } + }, + "disabled": { + "control": { "type": "boolean" } + }, + "small": { + "control": { "type": "boolean" } + } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + "legend": "Légende pour l’ensemble de champs", + "name": "radio", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ], + "state": "default", + "stateRelatedMessage": "State description" +}); + +export const Horizontal = getStory({ + "legend": "Légende pour l’ensemble de champs", + "name": "radio", + "orientation": "horizontal", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const WithHintText = getStory({ + "legend": "Légende pour l’ensemble de champs", + "name": "radio", + "hintText": "Texte de description additionnel", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const WithIndividualHints = getStory({ + "legend": "Légende pour l’ensemble de champs", + "name": "radio", + "options": [ + { + "label": "Label radio", + "hintText": "Texte de description additionnel", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "hintText": "Texte de description additionnel", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "hintText": "Texte de description additionnel", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const ErrorState = getStory({ + "legend": "Légende pour l’ensemble de champs", + "state": "error", + "stateRelatedMessage": "Texte d’erreur obligatoire", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const HorizontalErrorState = getStory({ + "legend": "Légende pour l’ensemble de champs", + "state": "error", + "orientation": "horizontal", + "stateRelatedMessage": "Texte d’erreur obligatoire", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const SuccessState = getStory({ + "legend": "Légende pour l’ensemble de champs", + "state": "success", + "orientation": "horizontal", + "stateRelatedMessage": "Texte de validation", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const Disabled = getStory({ + "legend": "Légende pour l’ensemble de champs", + "disabled": true, + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); + +export const Small = getStory({ + "legend": "Légende pour l’ensemble de champs", + "small": true, + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ] +}); From 765f23343f8109d18a094819f1dbc67f96252a66 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 18 Dec 2023 19:11:26 +0100 Subject: [PATCH 3/4] Review radio-rich-button --- src/RadioButtons.tsx | 2 +- src/RadioRichButtons.tsx | 2 +- src/shared/Fieldset.tsx | 60 +++++++++++++++++++--------------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/RadioButtons.tsx b/src/RadioButtons.tsx index ce36f581a..e9bfb6b73 100644 --- a/src/RadioButtons.tsx +++ b/src/RadioButtons.tsx @@ -2,7 +2,7 @@ import React, { memo, forwardRef } from "react"; import { symToStr } from "tsafe/symToStr"; import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; -export type RadioButtonsProps = Omit; +export type RadioButtonsProps = Omit; /** @see */ export const RadioButtons = memo( diff --git a/src/RadioRichButtons.tsx b/src/RadioRichButtons.tsx index 1f20093c3..b8ecb252c 100644 --- a/src/RadioRichButtons.tsx +++ b/src/RadioRichButtons.tsx @@ -2,7 +2,7 @@ import React, { memo, forwardRef } from "react"; import { symToStr } from "tsafe/symToStr"; import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; -export type RadioRichButtonsProps = Omit; +export type RadioRichButtonsProps = Omit; /** @see */ export const RadioRichButtons = memo( diff --git a/src/shared/Fieldset.tsx b/src/shared/Fieldset.tsx index 9ebdc1678..8d03c8e7e 100644 --- a/src/shared/Fieldset.tsx +++ b/src/shared/Fieldset.tsx @@ -4,8 +4,7 @@ import React, { forwardRef, type ReactNode, type CSSProperties, - type InputHTMLAttributes, - type DetailedHTMLProps + type ComponentProps } from "react"; import { symToStr } from "tsafe/symToStr"; import { assert } from "tsafe/assert"; @@ -14,20 +13,9 @@ import { cx } from "../tools/cx"; import { fr } from "../fr"; import { useAnalyticsId } from "../tools/useAnalyticsId"; -export type FieldsetProps = FieldsetProps.Radio | FieldsetProps.Checkbox | FieldsetProps.RadioRich; +export type FieldsetProps = FieldsetProps.Radio | FieldsetProps.Checkbox; export namespace FieldsetProps { - export type RegularOption = { - label: ReactNode; - hintText?: ReactNode; - nativeInputProps: DetailedHTMLProps< - InputHTMLAttributes, - HTMLInputElement - >; - }; - export type RadioRichOption = RegularOption & { - illustration: ReactNode; - }; export type Common = { className?: string; id?: string; @@ -35,6 +23,11 @@ export namespace FieldsetProps { style?: CSSProperties; legend?: ReactNode; hintText?: ReactNode; + options: { + label: ReactNode; + hintText?: ReactNode; + nativeInputProps: ComponentProps<"input">; + }[]; /** Default: "vertical" */ orientation?: "vertical" | "horizontal"; @@ -51,25 +44,30 @@ export namespace FieldsetProps { small?: boolean; }; - export type Radio = Common & { - type: "radio"; - name?: string; - rich?: false; - options: RegularOption[]; - }; + export type Radio = Radio.Regular | Radio.Rich; + + export namespace Radio { + type CommonRadio = Common & { + type: "radio"; + name?: string; + }; + + export type Regular = CommonRadio & { + rich?: false; + }; + + export type Rich = CommonRadio & { + rich: true; + options: (Common["options"][number] & { + illustration: ReactNode; + })[]; + }; + } export type Checkbox = Common & { type: "checkbox"; name?: never; - rich?: false; - options: RegularOption[]; - }; - - export type RadioRich = Common & { - type: "radio"; - name?: string; - rich: true; - options: RadioRichOption[]; + rich?: never; }; } @@ -165,7 +163,7 @@ export const Fieldset = memo(
{hintText} )} - {rich && ( + {!!rich && (
{options[i].illustration}
From ab1371c223b15429acacf18c11df393ea5926035 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 18 Dec 2023 19:37:49 +0100 Subject: [PATCH 4/4] Do not use a separate component for RichRadioButton --- src/RadioButtons.tsx | 2 +- src/RadioRichButtons.tsx | 16 - src/shared/Fieldset.tsx | 40 +-- stories/RadioButtons.stories.tsx | 31 ++ stories/RadioRichButtons.stories.tsx | 424 --------------------------- 5 files changed, 47 insertions(+), 466 deletions(-) delete mode 100644 src/RadioRichButtons.tsx delete mode 100644 stories/RadioRichButtons.stories.tsx diff --git a/src/RadioButtons.tsx b/src/RadioButtons.tsx index e9bfb6b73..ce36f581a 100644 --- a/src/RadioButtons.tsx +++ b/src/RadioButtons.tsx @@ -2,7 +2,7 @@ import React, { memo, forwardRef } from "react"; import { symToStr } from "tsafe/symToStr"; import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; -export type RadioButtonsProps = Omit; +export type RadioButtonsProps = Omit; /** @see */ export const RadioButtons = memo( diff --git a/src/RadioRichButtons.tsx b/src/RadioRichButtons.tsx deleted file mode 100644 index b8ecb252c..000000000 --- a/src/RadioRichButtons.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React, { memo, forwardRef } from "react"; -import { symToStr } from "tsafe/symToStr"; -import { Fieldset, type FieldsetProps } from "./shared/Fieldset"; - -export type RadioRichButtonsProps = Omit; - -/** @see */ -export const RadioRichButtons = memo( - forwardRef((props, ref) => ( -
- )) -); - -RadioRichButtons.displayName = symToStr({ RadioRichButtons }); - -export default RadioRichButtons; diff --git a/src/shared/Fieldset.tsx b/src/shared/Fieldset.tsx index 8d03c8e7e..3396747f5 100644 --- a/src/shared/Fieldset.tsx +++ b/src/shared/Fieldset.tsx @@ -44,30 +44,17 @@ export namespace FieldsetProps { small?: boolean; }; - export type Radio = Radio.Regular | Radio.Rich; - - export namespace Radio { - type CommonRadio = Common & { - type: "radio"; - name?: string; - }; - - export type Regular = CommonRadio & { - rich?: false; - }; - - export type Rich = CommonRadio & { - rich: true; - options: (Common["options"][number] & { - illustration: ReactNode; - })[]; - }; - } + export type Radio = Omit & { + type: "radio"; + name?: string; + options: (Common["options"][number] & { + illustration?: ReactNode; + })[]; + }; export type Checkbox = Common & { type: "checkbox"; name?: never; - rich?: never; }; } @@ -89,10 +76,13 @@ export const Fieldset = memo( type, name: name_props, small = false, - rich, ...rest } = props; + const isRichRadio = + type === "radio" && + options.find(options => options.illustration !== undefined) !== undefined; + assert>(); const id = useAnalyticsId({ @@ -159,11 +149,11 @@ export const Fieldset = memo( )}
- {options.map(({ label, hintText, nativeInputProps }, i) => ( + {options.map(({ label, hintText, nativeInputProps, ...rest }, i) => (
{hintText} )} - {!!rich && ( + {"illustration" in rest && (
- {options[i].illustration} + {rest.illustration}
)}
diff --git a/stories/RadioButtons.stories.tsx b/stories/RadioButtons.stories.tsx index 45635da32..a131f4bab 100644 --- a/stories/RadioButtons.stories.tsx +++ b/stories/RadioButtons.stories.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { RadioButtons, type RadioButtonsProps } from "../dist/RadioButtons"; import { sectionName } from "./sectionName"; import { getStoryFactory } from "./getStory"; @@ -384,3 +385,33 @@ export const Small = getStory({ } ] }); + +export const Rich = getStory({ + "legend": "Légende pour l’ensemble de champs", + "name": "radio", + "options": [ + { + "label": "Label radio", + "nativeInputProps": { + "value": "value1" + }, + "illustration": illustration + }, + { + "label": "Label radio 2", + "nativeInputProps": { + "value": "value2" + }, + "illustration": illustration + }, + { + "label": "Label radio 3", + "nativeInputProps": { + "value": "value3" + }, + "illustration": illustration + } + ], + "state": "default", + "stateRelatedMessage": "State description" +}); diff --git a/stories/RadioRichButtons.stories.tsx b/stories/RadioRichButtons.stories.tsx deleted file mode 100644 index 9de928a38..000000000 --- a/stories/RadioRichButtons.stories.tsx +++ /dev/null @@ -1,424 +0,0 @@ -import React from "react"; -import { RadioRichButtons, type RadioRichButtonsProps } from "../dist/RadioRichButtons"; -import { sectionName } from "./sectionName"; -import { getStoryFactory } from "./getStory"; -import { assert } from "tsafe/assert"; -import type { Equals } from "tsafe"; - -const { meta, getStory } = getStoryFactory({ - sectionName, - "wrappedComponent": { RadioRichButtons }, - "description": ` -- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-radio-riche) -- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/RadioRichButtons.tsx) - -## Controlled - -\`\`\`tsx -import { useState } from "react"; -import { RadioRichButtons } from "@codegouvfr/react-dsfr/RadioRichButtons"; - -function MyComponent(){ - - const [ value, setValue ] = useState<"one" | "two" | "three" | undefined>(undefined); - - return ( - setValue("one") - }, - illustration: illustration - }, - { - label: "Label radio 2", - nativeInputProps: { - checked: value === "two" - onChange: ()=> setValue("two") - }, - illustration: illustration - }, - { - label: "Label radio 3", - nativeInputProps: { - checked: value === "three" - onChange: ()=> setValue("three") - }, - illustration: illustration - } - ]} - /> - ); - -} -\`\`\` - -## Uncontrolled - -\`\`\`tsx -import { useState } from "react"; -import { RadioRichButtons } from "@codegouvfr/react-dsfr/RadioRichButtons"; - -function MyComponent(){ - - return ( -
- - }, - { - label: "Label radio 2", - nativeInputProps: { - value: "two" - }, - illustration: illustration - }, - { - label: "Label radio 3", - nativeInputProps: { - value: "three" - }, - illustration: illustration - } - ]} - /> - - ); - -} -\`\`\` - - -`, - "argTypes": { - "name": { - "description": - "The name that will be applied to all the underlying ``", - "control": { "type": "text" } - }, - "options": { - "description": `An array describing the radio options. - \`nativeInputProps\` is an object that you would pass as prop to \`\`, - this is where you define the value for each option. - \`illustration\` is an mandatory React element that will be displayed on the right of the label. - `, - "control": { "type": "null" } - }, - "orientation": { - "description": "Default: 'vertical'", - "options": (() => { - const options = ["horizontal", "vertical"] as const; - - assert< - Equals - >(); - - return options; - })(), - "control": { "type": "radio" } - }, - "state": { - "description": "Default: 'default'", - "options": (() => { - const options = ["success", "error", "default"] as const; - - assert< - Equals - >(); - - return options; - })(), - "control": { "type": "radio" } - }, - "stateRelatedMessage": { - "description": `The message won't be displayed if state is "default". - If the state is "error" providing a message is mandatory`, - "control": { "type": "text" } - }, - "disabled": { - "control": { "type": "boolean" } - }, - "small": { - "control": { "type": "boolean" } - } - }, - "disabledProps": ["lang"] -}); - -export default meta; - -export const Default = getStory({ - "legend": "Légende pour l’ensemble de champs", - "name": "radio", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ], - "state": "default", - "stateRelatedMessage": "State description" -}); - -export const Horizontal = getStory({ - "legend": "Légende pour l’ensemble de champs", - "name": "radio", - "orientation": "horizontal", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const WithHintText = getStory({ - "legend": "Légende pour l’ensemble de champs", - "name": "radio", - "hintText": "Texte de description additionnel", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const WithIndividualHints = getStory({ - "legend": "Légende pour l’ensemble de champs", - "name": "radio", - "options": [ - { - "label": "Label radio", - "hintText": "Texte de description additionnel", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "hintText": "Texte de description additionnel", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "hintText": "Texte de description additionnel", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const ErrorState = getStory({ - "legend": "Légende pour l’ensemble de champs", - "state": "error", - "stateRelatedMessage": "Texte d’erreur obligatoire", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const HorizontalErrorState = getStory({ - "legend": "Légende pour l’ensemble de champs", - "state": "error", - "orientation": "horizontal", - "stateRelatedMessage": "Texte d’erreur obligatoire", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const SuccessState = getStory({ - "legend": "Légende pour l’ensemble de champs", - "state": "success", - "orientation": "horizontal", - "stateRelatedMessage": "Texte de validation", - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const Disabled = getStory({ - "legend": "Légende pour l’ensemble de champs", - "disabled": true, - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -}); - -export const Small = getStory({ - "legend": "Légende pour l’ensemble de champs", - "small": true, - "options": [ - { - "label": "Label radio", - "nativeInputProps": { - "value": "value1" - }, - "illustration": illustration - }, - { - "label": "Label radio 2", - "nativeInputProps": { - "value": "value2" - }, - "illustration": illustration - }, - { - "label": "Label radio 3", - "nativeInputProps": { - "value": "value3" - }, - "illustration": illustration - } - ] -});