Skip to content

Commit 77aee0e

Browse files
authored
docs(headless): adopt Popover stories to css modules (#36103)
1 parent df3355d commit 77aee0e

13 files changed

Lines changed: 538 additions & 268 deletions

packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningCoverTarget.stories.tsx

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,61 @@ import * as React from 'react';
22
import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover';
33
import type { PositioningProps } from '@fluentui/react-headless-components-preview/positioning';
44

5-
const classes = {
6-
outer: 'w-full overflow-auto',
7-
wrapper: 'grid grid-cols-[repeat(3,auto)] grid-rows-[repeat(5,auto)] gap-16 mx-32 my-16 w-max',
8-
trigger:
9-
'h-12 w-32 flex items-center justify-center px-3 rounded-md bg-blue-600 text-white text-xs font-medium hover:bg-blue-700 cursor-pointer border-none',
10-
surface:
11-
'bg-white/95 rounded-lg shadow-lg border border-gray-200 px-4 py-3 text-sm w-56 h-28 flex items-center justify-center',
12-
};
5+
import styles from './positioning.module.css';
136

14-
const cells: Array<{
7+
const options: Array<{
158
label: string;
169
position: NonNullable<PositioningProps['position']>;
1710
align: NonNullable<PositioningProps['align']>;
18-
gridClass: string;
1911
}> = [
20-
{ label: 'above-start', position: 'above', align: 'start', gridClass: 'row-start-1 col-start-1' },
21-
{ label: 'above', position: 'above', align: 'center', gridClass: 'row-start-1 col-start-2' },
22-
{ label: 'above-end', position: 'above', align: 'end', gridClass: 'row-start-1 col-start-3' },
23-
{ label: 'before-top', position: 'before', align: 'start', gridClass: 'row-start-2 col-start-1' },
24-
{ label: 'before', position: 'before', align: 'center', gridClass: 'row-start-3 col-start-1' },
25-
{ label: 'before-bottom', position: 'before', align: 'end', gridClass: 'row-start-4 col-start-1' },
26-
{ label: 'after-top', position: 'after', align: 'start', gridClass: 'row-start-2 col-start-3' },
27-
{ label: 'after', position: 'after', align: 'center', gridClass: 'row-start-3 col-start-3' },
28-
{ label: 'after-bottom', position: 'after', align: 'end', gridClass: 'row-start-4 col-start-3' },
29-
{ label: 'below-start', position: 'below', align: 'start', gridClass: 'row-start-5 col-start-1' },
30-
{ label: 'below', position: 'below', align: 'center', gridClass: 'row-start-5 col-start-2' },
31-
{ label: 'below-end', position: 'below', align: 'end', gridClass: 'row-start-5 col-start-3' },
12+
{ label: 'above-start', position: 'above', align: 'start' },
13+
{ label: 'above', position: 'above', align: 'center' },
14+
{ label: 'above-end', position: 'above', align: 'end' },
15+
{ label: 'before-top', position: 'before', align: 'start' },
16+
{ label: 'before', position: 'before', align: 'center' },
17+
{ label: 'before-bottom', position: 'before', align: 'end' },
18+
{ label: 'after-top', position: 'after', align: 'start' },
19+
{ label: 'after', position: 'after', align: 'center' },
20+
{ label: 'after-bottom', position: 'after', align: 'end' },
21+
{ label: 'below-start', position: 'below', align: 'start' },
22+
{ label: 'below', position: 'below', align: 'center' },
23+
{ label: 'below-end', position: 'below', align: 'end' },
3224
];
3325

34-
export const CoverTarget = (): React.ReactNode => (
35-
<div className={classes.outer}>
36-
<div className={classes.wrapper}>
37-
{cells.map(cell => (
38-
<div key={cell.label} className={cell.gridClass}>
39-
<Popover positioning={{ position: cell.position, align: cell.align, coverTarget: true }}>
26+
export const CoverTarget = (): React.ReactNode => {
27+
const [selected, setSelected] = React.useState(options[1]);
28+
29+
return (
30+
<div className={styles.outer}>
31+
<div className={styles.layout}>
32+
<label className={styles.controls}>
33+
Position
34+
<select
35+
className={styles.select}
36+
value={selected.label}
37+
onChange={e => {
38+
const next = options.find(o => o.label === e.target.value);
39+
if (next) {
40+
setSelected(next);
41+
}
42+
}}
43+
>
44+
{options.map(option => (
45+
<option key={option.label} value={option.label}>
46+
{option.label}
47+
</option>
48+
))}
49+
</select>
50+
</label>
51+
<div className={styles.stage}>
52+
<Popover open positioning={{ position: selected.position, align: selected.align, coverTarget: true }}>
4053
<PopoverTrigger>
41-
<button className={classes.trigger}>{cell.label}</button>
54+
<button className={`${styles.trigger} ${styles.triggerFixed}`}>{selected.label}</button>
4255
</PopoverTrigger>
43-
<PopoverSurface className={classes.surface}>Container</PopoverSurface>
56+
<PopoverSurface className={styles.surfaceCover}>Container</PopoverSurface>
4457
</Popover>
4558
</div>
46-
))}
59+
</div>
4760
</div>
48-
</div>
49-
);
61+
);
62+
};

packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningDefault.stories.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@ import * as React from 'react';
22
import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover';
33
import type { PositioningProps } from '@fluentui/react-headless-components-preview/positioning';
44

5-
const classes = {
6-
trigger:
7-
'px-4 py-2 rounded-md bg-blue-600 text-white font-medium hover:bg-blue-700 data-[open]:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none',
8-
surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[160px]',
9-
};
5+
import styles from './positioning.module.css';
106

117
export const Default = (props: PositioningProps): React.ReactNode => (
128
<Popover positioning={props}>
139
<PopoverTrigger>
14-
<button className={classes.trigger}>Click me</button>
10+
<button className={styles.trigger}>Click me</button>
1511
</PopoverTrigger>
16-
<PopoverSurface className={classes.surface}>Container</PopoverSurface>
12+
<PopoverSurface className={styles.surfaceCallout}>Container</PopoverSurface>
1713
</Popover>
1814
);
1915

packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFallbackPositions.stories.tsx

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,86 @@
11
import * as React from 'react';
2-
import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox';
32
import { InlineAnchored } from './InlineAnchored';
43

5-
const classes = {
6-
page: 'flex flex-col gap-6 p-4',
7-
sectionTitle: 'text-sm font-semibold text-gray-800 mb-1',
8-
sectionNote: 'text-xs text-gray-500 max-w-xl mb-2',
9-
grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4',
10-
trigger:
11-
'px-3 py-1.5 rounded-md bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none',
12-
surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-3 min-w-[200px] max-w-xs text-sm',
13-
};
4+
import styles from './positioning.module.css';
145

156
export const FallbackPositions = (): React.ReactNode => (
16-
<div className={classes.page}>
17-
<style>{flipDemoSurfaceCss}</style>
18-
19-
<section>
20-
<h3 className={classes.sectionTitle}>Basic fallback</h3>
21-
<p className={classes.sectionNote}>
7+
<div className={styles.pageRoomy}>
8+
<section className={styles.section}>
9+
<h3 className={styles.sectionTitle}>Basic fallback</h3>
10+
<p className={styles.sectionNote}>
2211
Primary <code>above</code> overflows the box → first fallback <code>below-start</code> fits → surface renders
2312
there.
2413
</p>
25-
<div className={demoBoxClass} style={demoBoxStyle}>
14+
<div className={styles.demoBox}>
2615
<InlineAnchored
2716
positioning={{
2817
position: 'above',
2918
align: 'center',
3019
fallbackPositions: ['below-start', 'after'],
3120
}}
32-
surfaceClassName={classes.surface}
21+
surfaceClassName={`${styles.surfaceFallback} ${styles.flipReadout}`}
3322
trigger={
34-
<button
35-
className={classes.trigger}
36-
style={{ position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)' }}
37-
>
38-
trigger near top
39-
</button>
23+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopCenter}`}>trigger near top</button>
4024
}
4125
>
4226
<strong>Requested:</strong> above · fallbacks: <code>below-start</code>, <code>after</code>
4327
</InlineAnchored>
4428
</div>
4529
</section>
4630

47-
<section>
48-
<h3 className={classes.sectionTitle}>Chain walking</h3>
49-
<p className={classes.sectionNote}>
31+
<section className={styles.section}>
32+
<h3 className={styles.sectionTitle}>Chain walking</h3>
33+
<p className={styles.sectionNote}>
5034
Trigger pinned to top-left. Primary <code>above</code> overflows, first fallback <code>before</code> also
5135
overflows (no room to the left), so the browser falls through to <code>below</code>. The live{' '}
5236
<strong>Actual</strong> readout should read <code>below</code>.
5337
</p>
54-
<div className={demoBoxClass} style={demoBoxStyle}>
38+
<div className={styles.demoBox}>
5539
<InlineAnchored
5640
positioning={{
5741
position: 'above',
5842
align: 'center',
5943
fallbackPositions: ['before', 'below'],
6044
}}
61-
surfaceClassName={classes.surface}
45+
surfaceClassName={`${styles.surfaceFallback} ${styles.flipReadout}`}
6246
trigger={
63-
<button className={classes.trigger} style={{ position: 'absolute', top: 12, left: 12 }}>
64-
top-left trigger
65-
</button>
47+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopLeft}`}>top-left trigger</button>
6648
}
6749
>
6850
<strong>Requested:</strong> above · fallbacks: <code>before</code>, <code>below</code>
6951
</InlineAnchored>
7052
</div>
7153
</section>
7254

73-
<section>
74-
<h3 className={classes.sectionTitle}>Custom chain replaces default flip</h3>
75-
<p className={classes.sectionNote}>
55+
<section className={styles.section}>
56+
<h3 className={styles.sectionTitle}>Custom chain replaces default flip</h3>
57+
<p className={styles.sectionNote}>
7658
Same overflow condition, different chains. Left popover has no <code>fallbackPositions</code> → default{' '}
7759
<code>flip-block, flip-inline</code> fires → surface ends up below. Right popover passes <code>['after']</code>{' '}
7860
→ custom chain replaces defaults → surface goes to the right instead of flipping.
7961
</p>
80-
<div className={classes.grid}>
81-
<div className={demoBoxClass} style={demoBoxStyle}>
62+
<div className={styles.grid}>
63+
<div className={styles.demoBox}>
8264
<InlineAnchored
8365
positioning={{ position: 'above', align: 'center' }}
84-
surfaceClassName={classes.surface}
66+
surfaceClassName={`${styles.surfaceFallback} ${styles.flipReadout}`}
8567
trigger={
86-
<button
87-
className={classes.trigger}
88-
style={{ position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)' }}
89-
>
90-
default (flip)
91-
</button>
68+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopCenter}`}>default (flip)</button>
9269
}
9370
>
9471
<strong>Requested:</strong> above · no custom fallbacks
9572
</InlineAnchored>
9673
</div>
97-
<div className={demoBoxClass} style={demoBoxStyle}>
74+
<div className={styles.demoBox}>
9875
<InlineAnchored
9976
positioning={{
10077
position: 'above',
10178
align: 'center',
10279
fallbackPositions: ['after', 'below'],
10380
}}
104-
surfaceClassName={classes.surface}
81+
surfaceClassName={`${styles.surfaceFallback} ${styles.flipReadout}`}
10582
trigger={
106-
<button
107-
className={classes.trigger}
108-
style={{ position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)' }}
109-
>
83+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopCenter}`}>
11084
custom ({`['after']`})
11185
</button>
11286
}

packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingBlock.stories.tsx

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,30 @@
11
import * as React from 'react';
2-
import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox';
32
import { InlineAnchored } from './InlineAnchored';
43

54
import descriptionMd from './PositioningFlippingBlockDescription.md';
6-
7-
const classes = {
8-
page: 'flex flex-col gap-4 p-4',
9-
grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4',
10-
trigger:
11-
'px-3 py-1.5 rounded-md bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none',
12-
surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-2 w-[160px] text-xs',
13-
};
5+
import styles from './positioning.module.css';
146

157
export const FlippingBlock = (): React.ReactNode => (
16-
<div className={classes.page}>
17-
<style>{flipDemoSurfaceCss}</style>
18-
19-
<div className={classes.grid}>
20-
<div className={demoBoxClass} style={demoBoxStyle}>
8+
<div className={styles.page}>
9+
<div className={styles.grid}>
10+
<div className={styles.demoBox}>
2111
<InlineAnchored
2212
positioning={{ position: 'above' }}
23-
surfaceClassName={classes.surface}
13+
surfaceClassName={`${styles.surfaceFlipNarrow} ${styles.flipReadout}`}
2414
trigger={
25-
<button
26-
className={classes.trigger}
27-
style={{ position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)' }}
28-
>
29-
trigger near top
30-
</button>
15+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopCenter}`}>trigger near top</button>
3116
}
3217
>
3318
<strong>Requested:</strong> above → flips below
3419
</InlineAnchored>
3520
</div>
3621

37-
<div className={demoBoxClass} style={demoBoxStyle}>
22+
<div className={styles.demoBox}>
3823
<InlineAnchored
3924
positioning={{ position: 'below' }}
40-
surfaceClassName={classes.surface}
25+
surfaceClassName={`${styles.surfaceFlipNarrow} ${styles.flipReadout}`}
4126
trigger={
42-
<button
43-
className={classes.trigger}
44-
style={{ position: 'absolute', bottom: 12, left: '50%', transform: 'translateX(-50%)' }}
45-
>
27+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinBottomCenter}`}>
4628
trigger near bottom
4729
</button>
4830
}

packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingCorner.stories.tsx

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
import * as React from 'react';
2-
import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox';
32
import { InlineAnchored } from './InlineAnchored';
43

54
import descriptionMd from './PositioningFlippingCornerDescription.md';
6-
7-
const classes = {
8-
page: 'flex flex-col gap-4 p-4',
9-
grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4',
10-
trigger:
11-
'px-3 py-1.5 rounded-md bg-blue-600 text-white text-xs font-medium hover:bg-blue-700 focus-visible:outline-2 focus-visible:outline-blue-500 focus-visible:outline-offset-2 cursor-pointer border-none',
12-
surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-3 w-[320px] max-h-[140px] text-sm',
13-
};
5+
import styles from './positioning.module.css';
146

157
export const FlippingCorner = (): React.ReactNode => (
16-
<div className={classes.page}>
17-
<style>{flipDemoSurfaceCss}</style>
18-
19-
<div className={classes.grid}>
20-
<div className={demoBoxClass} style={demoBoxStyle}>
8+
<div className={styles.page}>
9+
<div className={styles.grid}>
10+
<div className={styles.demoBox}>
2111
<InlineAnchored
2212
positioning={{ position: 'above', align: 'end' }}
23-
surfaceClassName={classes.surface}
13+
surfaceClassName={`${styles.surfaceFlipWide} ${styles.flipReadout}`}
2414
trigger={
25-
<button className={classes.trigger} style={{ position: 'absolute', top: 12, left: 12 }}>
15+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopLeft}`}>
2616
top-left · requested above-end
2717
</button>
2818
}
@@ -31,12 +21,12 @@ export const FlippingCorner = (): React.ReactNode => (
3121
</InlineAnchored>
3222
</div>
3323

34-
<div className={demoBoxClass} style={demoBoxStyle}>
24+
<div className={styles.demoBox}>
3525
<InlineAnchored
3626
positioning={{ position: 'above', align: 'start' }}
37-
surfaceClassName={classes.surface}
27+
surfaceClassName={`${styles.surfaceFlipWide} ${styles.flipReadout}`}
3828
trigger={
39-
<button className={classes.trigger} style={{ position: 'absolute', top: 12, right: 12 }}>
29+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinTopRight}`}>
4030
top-right · requested above-start
4131
</button>
4232
}
@@ -45,12 +35,12 @@ export const FlippingCorner = (): React.ReactNode => (
4535
</InlineAnchored>
4636
</div>
4737

48-
<div className={demoBoxClass} style={demoBoxStyle}>
38+
<div className={styles.demoBox}>
4939
<InlineAnchored
5040
positioning={{ position: 'below', align: 'end' }}
51-
surfaceClassName={classes.surface}
41+
surfaceClassName={`${styles.surfaceFlipWide} ${styles.flipReadout}`}
5242
trigger={
53-
<button className={classes.trigger} style={{ position: 'absolute', bottom: 12, left: 12 }}>
43+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinBottomLeft}`}>
5444
bottom-left · requested below-end
5545
</button>
5646
}
@@ -59,12 +49,12 @@ export const FlippingCorner = (): React.ReactNode => (
5949
</InlineAnchored>
6050
</div>
6151

62-
<div className={demoBoxClass} style={demoBoxStyle}>
52+
<div className={styles.demoBox}>
6353
<InlineAnchored
6454
positioning={{ position: 'below', align: 'start' }}
65-
surfaceClassName={classes.surface}
55+
surfaceClassName={`${styles.surfaceFlipWide} ${styles.flipReadout}`}
6656
trigger={
67-
<button className={classes.trigger} style={{ position: 'absolute', bottom: 12, right: 12 }}>
57+
<button className={`${styles.trigger} ${styles.triggerSm} ${styles.pinBottomRight}`}>
6858
bottom-right · requested below-start
6959
</button>
7060
}

0 commit comments

Comments
 (0)