Skip to content

Commit e19d0c9

Browse files
committed
Feat: CallOut component
1 parent ca10a24 commit e19d0c9

File tree

6 files changed

+201
-4
lines changed

6 files changed

+201
-4
lines changed

COMPONENTS.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@
1717
- [x] Header
1818
- [x] Breadcrumb
1919
- [ ] Consent banner
20-
- [ ] Favicon (?)
2120
- [x] Stepper
2221
- [x] Toggle switch
2322
- [ ] Follow
2423
- [ ] Link
2524
- [x] SkipLinks
2625
- [ ] Select
2726
- [ ] Side menu
28-
- [ ] Call out
27+
- [x] Call out
2928
- [x] Highlight
3029
- [ ] Modal
3130
- [x] Main navigation

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 15/42, [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 16/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 (15/42, [see details](COMPONENTS.md))
48+
- [ ] All [the components](https://www.systeme-de-design.gouv.fr/elements-d-interface) are implemented (16/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
"./Footer": "./dist/Footer.js",
150150
"./Display": "./dist/Display.js",
151151
"./Card": "./dist/Card.js",
152+
"./CallOut": "./dist/CallOut.js",
152153
"./ButtonsGroup": "./dist/ButtonsGroup.js",
153154
"./Button": "./dist/Button.js",
154155
"./Breadcrumb": "./dist/Breadcrumb.js",

src/CallOut.tsx

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { memo, forwardRef, ReactNode } from "react";
2+
import { symToStr } from "tsafe/symToStr";
3+
import { assert } from "tsafe/assert";
4+
import type { Equals } from "tsafe";
5+
import type {
6+
FrClassName,
7+
FrIconClassName,
8+
RiIconClassName
9+
} from "./fr/generatedFromCss/classNames";
10+
import type { ButtonProps } from "./Button";
11+
import { Button } from "./Button";
12+
import { cx } from "./tools/cx";
13+
import { fr } from "./fr";
14+
15+
export type CallOutProps = {
16+
className?: string;
17+
iconId?: FrIconClassName | RiIconClassName;
18+
title?: ReactNode;
19+
description: ReactNode;
20+
buttonProps?: ButtonProps;
21+
colorVariant?: CallOutProps.ColorVariant;
22+
classes?: Partial<Record<"root" | "title" | "text" | "button", string>>;
23+
};
24+
25+
export namespace CallOutProps {
26+
type ExtractColorVariant<FrClassName> = FrClassName extends `fr-callout--${infer AccentColor}`
27+
? AccentColor
28+
: never;
29+
30+
export type ColorVariant = ExtractColorVariant<FrClassName>;
31+
}
32+
33+
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-callout> */
34+
export const CallOut = memo(
35+
forwardRef<HTMLDivElement, CallOutProps>((props, ref) => {
36+
const {
37+
className,
38+
iconId,
39+
title,
40+
description,
41+
buttonProps,
42+
colorVariant,
43+
classes = {},
44+
...rest
45+
} = props;
46+
47+
assert<Equals<keyof typeof rest, never>>();
48+
49+
return (
50+
<div
51+
className={cx(
52+
fr.cx(
53+
"fr-callout",
54+
iconId,
55+
colorVariant !== undefined && `fr-callout--${colorVariant}`
56+
),
57+
classes.root,
58+
className
59+
)}
60+
ref={ref}
61+
{...rest}
62+
>
63+
{title !== undefined && (
64+
<h3 className={cx(fr.cx("fr-callout__title"), classes.title)}>{title}</h3>
65+
)}
66+
<p className={cx(fr.cx("fr-callout__text"), classes.text)}> {description} </p>
67+
{buttonProps !== undefined && (
68+
<Button
69+
{...buttonProps}
70+
className={cx(classes.button, buttonProps.className)}
71+
/>
72+
)}
73+
</div>
74+
);
75+
})
76+
);
77+
78+
CallOut.displayName = symToStr({ CallOut });
79+
80+
export default CallOut;

stories/CallOut.stories.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { CallOut } from "../dist/CallOut";
2+
import type { CallOutProps } from "../dist/CallOut";
3+
import { sectionName } from "./sectionName";
4+
import { getStoryFactory, logCallbacks } from "./getStory";
5+
import { assert } from "tsafe/assert";
6+
import type { Equals } from "tsafe";
7+
8+
const { meta, getStory } = getStoryFactory({
9+
sectionName,
10+
"wrappedComponent": { CallOut },
11+
"description": `
12+
- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/mise-en-avant)
13+
- [See source code](//github.com/codegouvfr/react-dsfr/blob/main/src/CallOut.tsx)`,
14+
"disabledProps": ["lang"],
15+
"argTypes": {
16+
"title": {
17+
"description": "Optional"
18+
},
19+
"colorVariant": {
20+
"options": (() => {
21+
const options = [
22+
"green-tilleul-verveine",
23+
"green-bourgeon",
24+
"green-emeraude",
25+
"green-menthe",
26+
"green-archipel",
27+
"blue-ecume",
28+
"blue-cumulus",
29+
"purple-glycine",
30+
"pink-macaron",
31+
"pink-tuile",
32+
"brown-cafe-creme",
33+
"brown-caramel",
34+
"brown-opera",
35+
"orange-terre-battue",
36+
"yellow-moutarde",
37+
"yellow-tournesol",
38+
"beige-gris-galet",
39+
undefined
40+
] as const;
41+
42+
assert<Equals<typeof options[number], CallOutProps["colorVariant"]>>();
43+
44+
return options;
45+
})(),
46+
"control": { type: "select", labels: { null: "no severity" } }
47+
},
48+
"iconId": {
49+
"options": (() => {
50+
const options = [
51+
"ri-information-line",
52+
"fr-icon-checkbox-circle-line",
53+
"ri-ancient-gate-fill",
54+
undefined
55+
] as const;
56+
57+
assert<typeof options[number] extends CallOutProps["iconId"] ? true : false>();
58+
59+
return options;
60+
})(),
61+
"description": "Optional",
62+
"control": { "type": "radio" }
63+
},
64+
"buttonProps": {
65+
"description":
66+
"The same props you would pass to a `<Button />`, `undefined` if you don't want any button",
67+
"control": { "type": null }
68+
}
69+
}
70+
});
71+
72+
export default meta;
73+
74+
export const Default = getStory({
75+
"title": "Titre mise en avant",
76+
"iconId": "ri-information-line",
77+
"description": `Les parents d’enfants de 11 à 14 ans n’ont aucune démarche à
78+
accomplir : les CAF versent automatiquement l’ARS aux familles déjà allocataires
79+
qui remplissent les conditions.`,
80+
"colorVariant": undefined,
81+
"buttonProps": {
82+
"children": "Label bouton MD",
83+
...logCallbacks(["onClick"])
84+
}
85+
});
86+
87+
export const NoTitle = getStory({
88+
"iconId": "ri-information-line",
89+
"description": `Les parents d’enfants de 11 à 14 ans n’ont aucune démarche à
90+
accomplir : les CAF versent automatiquement l’ARS aux familles déjà allocataires
91+
qui remplissent les conditions.`,
92+
"colorVariant": undefined,
93+
"buttonProps": {
94+
"children": "Label bouton MD",
95+
...logCallbacks(["onClick"])
96+
}
97+
});
98+
99+
export const NoIcon = getStory({
100+
"title": "Titre mise en avant",
101+
"description": `Les parents d’enfants de 11 à 14 ans n’ont aucune démarche à
102+
accomplir : les CAF versent automatiquement l’ARS aux familles déjà allocataires
103+
qui remplissent les conditions.`,
104+
"colorVariant": undefined,
105+
"buttonProps": {
106+
"children": "Label bouton MD",
107+
...logCallbacks(["onClick"])
108+
}
109+
});
110+
111+
export const NoButton = getStory({
112+
"title": "Titre mise en avant",
113+
"iconId": "ri-information-line",
114+
"description": `Les parents d’enfants de 11 à 14 ans n’ont aucune démarche à
115+
accomplir : les CAF versent automatiquement l’ARS aux familles déjà allocataires
116+
qui remplissent les conditions.`
117+
});

0 commit comments

Comments
 (0)