Skip to content

Commit eb2dada

Browse files
authored
Merge pull request #25 from codegouvfr/stepper
feat: add Stepper component
2 parents c7df287 + 2a73840 commit eb2dada

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
"./mui": "./dist/mui.js",
125125
"./lib": "./dist/lib/index.js",
126126
"./Tabs": "./dist/Tabs.js",
127+
"./Stepper": "./dist/Stepper.js",
127128
"./SkipLinks": "./dist/SkipLinks.js",
128129
"./Notice": "./dist/Notice.js",
129130
"./Highlight": "./dist/Highlight.js",

src/Stepper.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React, { memo, forwardRef } from "react";
2+
import { symToStr } from "tsafe/symToStr";
3+
import { assert } from "tsafe/assert";
4+
import type { Equals } from "tsafe";
5+
import { fr } from "./lib";
6+
import { cx } from "./lib/tools/cx";
7+
8+
// We make users import dsfr.css, so we don't need to import the scoped CSS
9+
// but in the future if we have a complete component coverage it
10+
// we could stop requiring users to import the hole CSS and only import on a
11+
// per component basis.
12+
import "./dsfr/component/stepper/stepper.css";
13+
14+
export type StepperProps = {
15+
className?: string;
16+
currentStep: number;
17+
steps: number;
18+
title: string;
19+
nextTitle?: string;
20+
};
21+
22+
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-stepper> */
23+
export const Stepper = memo(
24+
forwardRef<HTMLDivElement, StepperProps>((props, ref) => {
25+
const { className, currentStep, steps, title, nextTitle, ...rest } = props;
26+
27+
assert<Equals<keyof typeof rest, never>>();
28+
29+
return (
30+
<div className={cx(fr.cx("fr-stepper"), className)} ref={ref}>
31+
<h2 className="fr-stepper__title">
32+
<span className="fr-stepper__state">
33+
Étape {currentStep} sur {steps}
34+
</span>
35+
{title}
36+
</h2>
37+
<div
38+
className="fr-stepper__steps"
39+
data-fr-current-step={currentStep}
40+
data-fr-steps={steps}
41+
></div>
42+
{nextTitle && (
43+
<p className="fr-stepper__details">
44+
<span className="fr-text--bold">Étape suivante :</span> {nextTitle}
45+
</p>
46+
)}
47+
</div>
48+
);
49+
})
50+
);
51+
52+
Stepper.displayName = symToStr({ Stepper });
53+
54+
export default Stepper;

stories/Stepper.stories.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Stepper } from "../dist/Stepper";
2+
import { sectionName } from "./sectionName";
3+
import { getStoryFactory } from "./getStory";
4+
5+
const { meta, getStory } = getStoryFactory({
6+
sectionName,
7+
"wrappedComponent": { Stepper },
8+
"description": `
9+
- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/indicateur-d-etape)
10+
- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Stepper.tsx)`,
11+
"disabledProps": ["lang"]
12+
});
13+
14+
export default meta;
15+
16+
export const Default = getStory({
17+
steps: 3,
18+
currentStep: 1,
19+
title: "Titre de l’étape en cours",
20+
nextTitle: "Titre de la prochaine étape"
21+
});
22+
23+
export const StepperWithoutNext = getStory({
24+
steps: 4,
25+
currentStep: 4,
26+
title: "Titre de la dernière étape en cours"
27+
});

0 commit comments

Comments
 (0)