Skip to content

Commit daf7a25

Browse files
Alexander KatrukhinAlexander Katrukhin
authored andcommitted
feat(carousel): add Design Best Practices story for Carousel component
1 parent 63a1c6b commit daf7a25

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { Button, CarouselSlider, Image, makeStyles, tokens, typographyStyles } from '@fluentui/react-components';
2+
import {
3+
Carousel,
4+
CarouselAnnouncerFunction,
5+
CarouselCard,
6+
CarouselNav,
7+
CarouselNavButton,
8+
CarouselNavContainer,
9+
CarouselViewport,
10+
} from '@fluentui/react-components';
11+
import * as React from 'react';
12+
13+
const useClasses = makeStyles({
14+
bannerCard: {
15+
alignContent: 'center',
16+
borderRadius: tokens.borderRadiusXLarge,
17+
boxShadow: tokens.shadow16,
18+
height: '450px',
19+
textAlign: 'left',
20+
position: 'relative',
21+
},
22+
image: {
23+
borderRadius: 'inherit',
24+
},
25+
cardContainer: {
26+
display: 'flex',
27+
flexDirection: 'column',
28+
gap: '8px',
29+
30+
position: 'absolute',
31+
left: '10%',
32+
top: '25%',
33+
borderRadius: tokens.borderRadiusLarge,
34+
boxShadow: tokens.shadow8,
35+
background: tokens.colorNeutralBackground1,
36+
padding: '24px 32px',
37+
maxWidth: '270px',
38+
width: '50%',
39+
},
40+
title: {
41+
...typographyStyles.title3,
42+
},
43+
subtext: {
44+
marginBottom: '12px',
45+
...typographyStyles.body1,
46+
},
47+
container: {
48+
display: 'grid',
49+
gridTemplateColumns: '1fr',
50+
gridTemplateRows: 'auto 1fr',
51+
},
52+
card: {
53+
minHeight: '100px',
54+
},
55+
carousel: {
56+
flex: 1,
57+
},
58+
controls: {
59+
display: 'flex',
60+
flexDirection: 'column',
61+
gap: '6px',
62+
63+
padding: '10px',
64+
},
65+
field: {
66+
flex: 1,
67+
gridTemplateColumns: 'minmax(100px, max-content) 1fr',
68+
},
69+
dropdown: {
70+
maxWidth: 'max-content',
71+
},
72+
carouselHeader: {
73+
display: 'flex',
74+
justifyContent: 'space-between',
75+
alignItems: 'center',
76+
gap: '10px',
77+
marginBottom: '16px',
78+
},
79+
carouselHeaderTitle: { flex: '1', margin: '0', fontSize: '22px', fontWeight: 800 },
80+
carouselNavigation: { width: 'fit-content', alignSelf: 'center', margin: '0' },
81+
slider: {
82+
gap: '0',
83+
'& > *': {
84+
margin: '0 12px',
85+
},
86+
},
87+
});
88+
89+
const IMAGES = [
90+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/sea-full-img.jpg',
91+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/bridge-full-img.jpg',
92+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/park-full-img.jpg',
93+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/sea-full-img.jpg',
94+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/bridge-full-img.jpg',
95+
'https://fabricweb.azureedge.net/fabric-website/assets/images/swatch-picker/park-full-img.jpg',
96+
];
97+
98+
const BannerCard: React.FC<{ children: React.ReactNode; imageSrc: string; index: number }> = props => {
99+
const { children, imageSrc, index } = props;
100+
const classes = useClasses();
101+
102+
return (
103+
<CarouselCard autoSize className={classes.bannerCard} aria-label={`${index + 1} of ${IMAGES.length}`}>
104+
<Image fit="cover" src={imageSrc} role="presentation" className={classes.image} />
105+
106+
<div className={classes.cardContainer}>
107+
<div className={classes.title}>{children}</div>
108+
<div className={classes.subtext}>
109+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
110+
magna aliqua. Ut enim ad minim veniam.
111+
</div>
112+
<div>
113+
<Button appearance="primary">Call to action</Button>
114+
</div>
115+
</div>
116+
</CarouselCard>
117+
);
118+
};
119+
120+
const getAnnouncement: CarouselAnnouncerFunction = (index: number, totalSlides: number, slideGroupList: number[][]) => {
121+
return `Carousel slide ${index + 1} of ${totalSlides}`;
122+
};
123+
124+
export const DesignBestPractices = () => {
125+
const classes = useClasses();
126+
127+
return (
128+
<div className={classes.container}>
129+
<div className={classes.card}>
130+
<Carousel circular draggable announcement={getAnnouncement}>
131+
<div className={classes.carouselHeader}>
132+
<h1 className={classes.carouselHeaderTitle}>Carousel Title</h1>
133+
<CarouselNavContainer
134+
next={{ 'aria-label': 'go to next' }}
135+
prev={{ 'aria-label': 'go to prev' }}
136+
className={classes.carouselNavigation}
137+
>
138+
<CarouselNav>{index => <CarouselNavButton aria-label={`Carousel Nav Button ${index}`} />}</CarouselNav>
139+
</CarouselNavContainer>
140+
</div>
141+
<CarouselViewport>
142+
<CarouselSlider className={classes.slider}>
143+
{IMAGES.map((imageSrc, index) => (
144+
<BannerCard key={`image-${index}`} imageSrc={imageSrc} index={index}>
145+
Card {index + 1}
146+
</BannerCard>
147+
))}
148+
</CarouselSlider>
149+
</CarouselViewport>
150+
</Carousel>
151+
</div>
152+
</div>
153+
);
154+
};
155+
156+
DesignBestPractices.parameters = {
157+
docs: {
158+
description: {
159+
story:
160+
'Carousel can have responsive cards that adjust their size based on the content, using `autoSize` prop on `CarouselCard`. This example demonstrates responsive banner cards with images.',
161+
},
162+
},
163+
};

packages/react-components/react-carousel/stories/src/Carousel/index.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import descriptionMd from './CarouselDescription.md';
1515
import bestPracticesMd from './CarouselBestPractices.md';
1616

1717
export { Default } from './CarouselDefault.stories';
18+
export { DesignBestPractices } from './CarouselDesignBestPractices.stories';
1819
export { Responsive } from './CarouselResponsive.stories';
1920
export { Controlled } from './CarouselControlled.stories';
2021
export { ImageSlideshow } from './CarouselImageBox.stories';

0 commit comments

Comments
 (0)