Skip to content

Commit 2c44737

Browse files
authored
Merge pull request #221 from codegouvfr/feat/card-ehancement
feat(card): add tier and half ratio, add image component
2 parents 12bad3e + 0089da7 commit 2c44737

File tree

2 files changed

+111
-10
lines changed

2 files changed

+111
-10
lines changed

src/Card.tsx

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@ export type CardProps = {
1717
title: ReactNode;
1818
titleAs?: `h${2 | 3 | 4 | 5 | 6}`;
1919
desc?: ReactNode;
20-
imageUrl?: string;
21-
imageAlt?: string;
2220
start?: ReactNode;
2321
detail?: ReactNode;
2422
end?: ReactNode;
2523
endDetail?: ReactNode;
26-
badge?: ReactNode;
2724
/** where actions can be placed */
2825
footer?: ReactNode;
2926
/** Default: "medium", only affect the text */
@@ -58,9 +55,9 @@ export type CardProps = {
5855
>
5956
>;
6057
style?: CSSProperties;
61-
/** Default false */
62-
horizontal?: boolean;
63-
} & (CardProps.EnlargedLink | CardProps.NotEnlargedLink);
58+
} & (CardProps.EnlargedLink | CardProps.NotEnlargedLink) &
59+
(CardProps.Horizontal | CardProps.Vertical) &
60+
(CardProps.WithImageLink | CardProps.WithImageComponent | CardProps.WithoutImage);
6461

6562
export namespace CardProps {
6663
export type EnlargedLink = {
@@ -73,6 +70,39 @@ export namespace CardProps {
7370
linkProps?: RegisteredLinkProps;
7471
iconId?: never;
7572
};
73+
74+
export type Horizontal = {
75+
/** Default false */
76+
horizontal: true;
77+
ratio?: "33/66" | "50/50";
78+
};
79+
80+
export type Vertical = {
81+
/** Default false */
82+
horizontal?: false;
83+
ratio?: never;
84+
};
85+
86+
export type WithImageLink = {
87+
badge?: ReactNode;
88+
imageUrl: string;
89+
imageAlt: string;
90+
imageComponent?: never;
91+
};
92+
93+
export type WithImageComponent = {
94+
badge?: ReactNode;
95+
imageUrl?: never;
96+
imageAlt?: never;
97+
imageComponent: ReactNode;
98+
};
99+
100+
export type WithoutImage = {
101+
badge?: never;
102+
imageUrl?: never;
103+
imageAlt?: never;
104+
imageComponent?: never;
105+
};
76106
}
77107

78108
/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-card> */
@@ -87,13 +117,15 @@ export const Card = memo(
87117
desc,
88118
imageUrl,
89119
imageAlt,
120+
imageComponent,
90121
start,
91122
detail,
92123
end,
93124
endDetail,
94125
badge,
95126
footer,
96127
horizontal = false,
128+
ratio,
97129
size = "medium",
98130
classes = {},
99131
enlargeLink = false,
@@ -123,6 +155,9 @@ export const Card = memo(
123155
"fr-card",
124156
enlargeLink && "fr-enlarge-link",
125157
horizontal && "fr-card--horizontal",
158+
horizontal &&
159+
ratio !== undefined &&
160+
`fr-card--horizontal-${ratio === "33/66" ? "tier" : "half"}`,
126161
(() => {
127162
switch (size) {
128163
case "large":
@@ -201,6 +236,18 @@ export const Card = memo(
201236
)}
202237
</div>
203238
)}
239+
{imageComponent !== undefined && (
240+
<div className={cx(fr.cx("fr-card__header"), classes.header)}>
241+
<div className={cx(fr.cx("fr-card__img"), classes.img)}>
242+
{imageComponent}
243+
</div>
244+
{badge !== undefined && (
245+
<ul className={cx(fr.cx("fr-badges-group"), classes.badge)}>
246+
<li>{badge}</li>
247+
</ul>
248+
)}
249+
</div>
250+
)}
204251
</div>
205252
);
206253
})

stories/Card.stories.tsx

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { getStoryFactory } from "./getStory";
77
import { assert } from "tsafe/assert";
88
import type { Equals } from "tsafe";
99

10+
import artworkOvoidSvgUrl from "../dist/dsfr/artwork/background/ovoid.svg";
11+
import artworkTechnicalErrorSvgUrl from "../dist/dsfr/artwork/pictograms/system/technical-error.svg";
12+
1013
import { fr } from "../dist";
1114

1215
const { meta, getStory } = getStoryFactory({
@@ -151,7 +154,7 @@ export const CardWithShadow = getStory(
151154
);
152155

153156
export const CardWithoutImage = getStory(
154-
{ ...defaultProps, "imageUrl": undefined },
157+
{ ...defaultProps },
155158
{ "description": "Carte sans image" }
156159
);
157160

@@ -310,6 +313,24 @@ export const CardHorizontal = getStory(
310313
{ "description": "Carte horizontale", "defaultContainerWidth": 700 }
311314
);
312315

316+
export const CardHorizontalTierRatio = getStory(
317+
{
318+
...defaultProps,
319+
"horizontal": true,
320+
ratio: "33/66"
321+
},
322+
{ "description": "Carte horizontale", "defaultContainerWidth": 700 }
323+
);
324+
325+
export const CardHorizontalHalfRatio = getStory(
326+
{
327+
...defaultProps,
328+
"horizontal": true,
329+
ratio: "50/50"
330+
},
331+
{ "description": "Carte horizontale", "defaultContainerWidth": 700 }
332+
);
333+
313334
export const CardHorizontalSM = getStory(
314335
{
315336
...defaultProps,
@@ -333,7 +354,6 @@ export const CardHorizontalWithoutImage = getStory(
333354
...defaultProps,
334355
"horizontal": true,
335356
"size": "large",
336-
"imageUrl": undefined,
337357
"start": (
338358
<ul className={fr.cx("fr-badges-group")}>
339359
<li>
@@ -353,8 +373,7 @@ export const CardHorizontalWithoutImageAndEnlargeLink = getStory(
353373
...defaultProps,
354374
"horizontal": true,
355375
"enlargeLink": false,
356-
"size": "large",
357-
"imageUrl": undefined
376+
"size": "large"
358377
},
359378
{ "description": "Carte horizontale sans image", "defaultContainerWidth": 900 }
360379
);
@@ -414,3 +433,38 @@ export const CardNoLink = getStory(
414433
},
415434
{ "description": "Carte horizontale sans lien", "defaultContainerWidth": 900 }
416435
);
436+
437+
export const CardWithImageComponent = getStory({
438+
...defaultProps,
439+
enlargeLink: false,
440+
imageUrl: undefined,
441+
imageAlt: undefined,
442+
imageComponent: (
443+
<svg
444+
xmlns="http://www.w3.org/2000/svg"
445+
className="fr-responsive-img fr-artwork"
446+
aria-hidden="true"
447+
viewBox="0 0 160 200"
448+
>
449+
<use className="fr-artwork-motif" href={`${artworkOvoidSvgUrl}#artwork-motif`}></use>
450+
<use
451+
className="fr-artwork-background"
452+
href={`${artworkOvoidSvgUrl}#artwork-background`}
453+
></use>
454+
<g transform="translate(40, 60)">
455+
<use
456+
className="fr-artwork-decorative"
457+
href={`${artworkTechnicalErrorSvgUrl}#artwork-decorative`}
458+
></use>
459+
<use
460+
className="fr-artwork-minor"
461+
href={`${artworkTechnicalErrorSvgUrl}#artwork-minor`}
462+
></use>
463+
<use
464+
className="fr-artwork-major"
465+
href={`${artworkTechnicalErrorSvgUrl}#artwork-major`}
466+
></use>
467+
</g>
468+
</svg>
469+
)
470+
});

0 commit comments

Comments
 (0)