diff --git a/change/@fluentui-react-headless-components-preview-f2171a9b-8a48-4ad5-84f2-0b897f173745.json b/change/@fluentui-react-headless-components-preview-f2171a9b-8a48-4ad5-84f2-0b897f173745.json new file mode 100644 index 0000000000000..8e3e54fe553ed --- /dev/null +++ b/change/@fluentui-react-headless-components-preview-f2171a9b-8a48-4ad5-84f2-0b897f173745.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: add Persona component", + "packageName": "@fluentui/react-headless-components-preview", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-headless-components-preview/library/bundle-size/AllComponents.fixture.js b/packages/react-components/react-headless-components-preview/library/bundle-size/AllComponents.fixture.js index e6052ed976c98..14b87aef5c243 100644 --- a/packages/react-components/react-headless-components-preview/library/bundle-size/AllComponents.fixture.js +++ b/packages/react-components/react-headless-components-preview/library/bundle-size/AllComponents.fixture.js @@ -14,6 +14,7 @@ import * as Input from '@fluentui/react-headless-components-preview/input'; import * as Link from '@fluentui/react-headless-components-preview/link'; import * as MessageBar from '@fluentui/react-headless-components-preview/message-bar'; import * as ProgressBar from '@fluentui/react-headless-components-preview/progress-bar'; +import * as Persona from '@fluentui/react-headless-components-preview/persona'; import * as Popover from '@fluentui/react-headless-components-preview/popover'; import * as Provider from '@fluentui/react-headless-components-preview/provider'; import * as RadioGroup from '@fluentui/react-headless-components-preview/radio-group'; @@ -48,8 +49,9 @@ console.log({ Input, Link, MessageBar, - ProgressBar, + Persona, Popover, + ProgressBar, Provider, RadioGroup, RatingDisplay, diff --git a/packages/react-components/react-headless-components-preview/library/etc/persona.api.md b/packages/react-components/react-headless-components-preview/library/etc/persona.api.md new file mode 100644 index 0000000000000..d1b5428dd4e99 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/etc/persona.api.md @@ -0,0 +1,40 @@ +## API Report File for "@fluentui/react-headless-components-preview" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { AvatarBaseProps } from '@fluentui/react-avatar'; +import type { ComponentProps } from '@fluentui/react-utilities'; +import type { ComponentState } from '@fluentui/react-utilities'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import type { JSXElement } from '@fluentui/react-utilities/'; +import type { PersonaProps as PersonaProps_2 } from '@fluentui/react-persona'; +import type { PersonaSlots as PersonaSlots_2 } from '@fluentui/react-persona'; +import type { PersonaState as PersonaState_2 } from '@fluentui/react-persona'; +import type * as React_2 from 'react'; +import type { Slot } from '@fluentui/react-utilities'; + +// @public +export const Persona: ForwardRefComponent; + +// @public +export type PersonaProps = ComponentProps & Pick; + +// @public (undocumented) +export type PersonaSlots = Omit & { + avatar?: Slot; +}; + +// @public +export type PersonaState = ComponentState & Pick; + +// @public (undocumented) +export const renderPersona: (state: PersonaState) => JSXElement; + +// @public +export const usePersona: (props: PersonaProps, ref: React_2.Ref) => PersonaState; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-components/react-headless-components-preview/library/package.json b/packages/react-components/react-headless-components-preview/library/package.json index 3575cae4292a4..a14d253f4f5b3 100644 --- a/packages/react-components/react-headless-components-preview/library/package.json +++ b/packages/react-components/react-headless-components-preview/library/package.json @@ -159,6 +159,12 @@ "import": "./lib/message-bar.js", "require": "./lib-commonjs/message-bar.js" }, + "./persona": { + "types": "./dist/persona.d.ts", + "node": "./lib-commonjs/persona.js", + "import": "./lib/persona.js", + "require": "./lib-commonjs/persona.js" + }, "./popover": { "types": "./dist/popover.d.ts", "node": "./lib-commonjs/popover.js", diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.test.tsx b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.test.tsx new file mode 100644 index 0000000000000..1157334274ca0 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.test.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { isConformant } from '../../testing/isConformant'; +import { Persona } from './Persona'; + +describe('Persona', () => { + isConformant({ + Component: Persona, + displayName: 'Persona', + }); + + it('renders a default state', () => { + const { getByRole, getByText } = render( + , + ); + + expect(getByRole('img', { name: 'John Doe' })).toBeInTheDocument(); + expect(getByText('John Doe')).toBeInTheDocument(); + expect(getByText('Software Engineer')).toBeInTheDocument(); + }); + + it('renders the avatar after the text when textPosition is before', () => { + const { container, getByText, getByRole } = render( + , + ); + + const root = container.firstElementChild as HTMLElement; + const primaryText = getByText('John Doe'); + const avatar = getByRole('img', { name: 'John Doe' }); + + expect(root.firstElementChild).toBe(primaryText); + expect(root.lastElementChild).toBe(avatar); + }); +}); diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.tsx b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.tsx new file mode 100644 index 0000000000000..d52df0507adea --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.tsx @@ -0,0 +1,18 @@ +'use client'; + +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { usePersona } from './usePersona'; +import { renderPersona } from './renderPersona'; +import type { PersonaProps } from './Persona.types'; + +/** + * Represents a person or with an avatar, primary text, and optional secondary text. + */ +export const Persona: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = usePersona(props, ref); + + return renderPersona(state); +}); + +Persona.displayName = 'Persona'; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.types.ts b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.types.ts new file mode 100644 index 0000000000000..0e2b786135191 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/Persona.types.ts @@ -0,0 +1,21 @@ +import type { + PersonaSlots as PersonaBaseSlots, + PersonaProps as PersonaBaseProps, + PersonaState as PersonaBaseState, +} from '@fluentui/react-persona'; +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { Avatar } from '../Avatar'; + +export type PersonaSlots = Omit & { + avatar?: Slot; +}; + +/** + * Persona Props + */ +export type PersonaProps = ComponentProps & Pick; + +/** + * State used in rendering Persona + */ +export type PersonaState = ComponentState & Pick; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/index.ts b/packages/react-components/react-headless-components-preview/library/src/components/Persona/index.ts new file mode 100644 index 0000000000000..de25a0319397c --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/index.ts @@ -0,0 +1,4 @@ +export { Persona } from './Persona'; +export type { PersonaSlots, PersonaProps, PersonaState } from './Persona.types'; +export { renderPersona } from './renderPersona'; +export { usePersona } from './usePersona'; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/renderPersona.tsx b/packages/react-components/react-headless-components-preview/library/src/components/Persona/renderPersona.tsx new file mode 100644 index 0000000000000..29bcb44309215 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/renderPersona.tsx @@ -0,0 +1,6 @@ +import { renderPersona_unstable } from '@fluentui/react-persona'; + +import type { PersonaState } from './Persona.types'; +import type { JSXElement } from '@fluentui/react-utilities/'; + +export const renderPersona = renderPersona_unstable as (state: PersonaState) => JSXElement; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Persona/usePersona.ts b/packages/react-components/react-headless-components-preview/library/src/components/Persona/usePersona.ts new file mode 100644 index 0000000000000..af4f72ec262ef --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/components/Persona/usePersona.ts @@ -0,0 +1,38 @@ +'use client'; + +import type * as React from 'react'; +import { usePersonaBase_unstable } from '@fluentui/react-persona'; +import { slot } from '@fluentui/react-utilities'; +import type { PersonaProps, PersonaState } from './Persona.types'; +import { Avatar } from '../Avatar'; + +/** + * Create the state required to render Persona. + * + * The returned state can be modified with hooks such as usePersonaStyles_unstable, + * before being passed to renderPersona_unstable. + * + * @param props - props from this instance of Persona + * @param ref - reference to root HTMLDivElement of Persona + */ +export const usePersona = (props: PersonaProps, ref: React.Ref): PersonaState => { + const { textPosition = 'after', ...baseProps } = props; + const baseState = usePersonaBase_unstable(baseProps, ref); + + return { + ...baseState, + textPosition, + components: { + // eslint-disable-next-line @typescript-eslint/no-deprecated + ...baseState.components, + avatar: Avatar, + }, + avatar: slot.optional(props.avatar, { + renderByDefault: true, + defaultProps: { + name: props.name, + }, + elementType: Avatar, + }), + }; +}; diff --git a/packages/react-components/react-headless-components-preview/library/src/persona.ts b/packages/react-components/react-headless-components-preview/library/src/persona.ts new file mode 100644 index 0000000000000..f592ab528aac6 --- /dev/null +++ b/packages/react-components/react-headless-components-preview/library/src/persona.ts @@ -0,0 +1,2 @@ +export { Persona, renderPersona, usePersona } from './components/Persona'; +export type { PersonaSlots, PersonaProps, PersonaState } from './components/Persona'; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningCoverTarget.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningCoverTarget.stories.tsx index 6ecac5e95d953..eb1d5b9bfb0af 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningCoverTarget.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningCoverTarget.stories.tsx @@ -2,48 +2,61 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; import type { PositioningProps } from '@fluentui/react-headless-components-preview/positioning'; -const classes = { - outer: 'w-full overflow-auto', - wrapper: 'grid grid-cols-[repeat(3,auto)] grid-rows-[repeat(5,auto)] gap-16 mx-32 my-16 w-max', - trigger: - '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', - surface: - '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', -}; +import styles from './positioning.module.css'; -const cells: Array<{ +const options: Array<{ label: string; position: NonNullable; align: NonNullable; - gridClass: string; }> = [ - { label: 'above-start', position: 'above', align: 'start', gridClass: 'row-start-1 col-start-1' }, - { label: 'above', position: 'above', align: 'center', gridClass: 'row-start-1 col-start-2' }, - { label: 'above-end', position: 'above', align: 'end', gridClass: 'row-start-1 col-start-3' }, - { label: 'before-top', position: 'before', align: 'start', gridClass: 'row-start-2 col-start-1' }, - { label: 'before', position: 'before', align: 'center', gridClass: 'row-start-3 col-start-1' }, - { label: 'before-bottom', position: 'before', align: 'end', gridClass: 'row-start-4 col-start-1' }, - { label: 'after-top', position: 'after', align: 'start', gridClass: 'row-start-2 col-start-3' }, - { label: 'after', position: 'after', align: 'center', gridClass: 'row-start-3 col-start-3' }, - { label: 'after-bottom', position: 'after', align: 'end', gridClass: 'row-start-4 col-start-3' }, - { label: 'below-start', position: 'below', align: 'start', gridClass: 'row-start-5 col-start-1' }, - { label: 'below', position: 'below', align: 'center', gridClass: 'row-start-5 col-start-2' }, - { label: 'below-end', position: 'below', align: 'end', gridClass: 'row-start-5 col-start-3' }, + { label: 'above-start', position: 'above', align: 'start' }, + { label: 'above', position: 'above', align: 'center' }, + { label: 'above-end', position: 'above', align: 'end' }, + { label: 'before-top', position: 'before', align: 'start' }, + { label: 'before', position: 'before', align: 'center' }, + { label: 'before-bottom', position: 'before', align: 'end' }, + { label: 'after-top', position: 'after', align: 'start' }, + { label: 'after', position: 'after', align: 'center' }, + { label: 'after-bottom', position: 'after', align: 'end' }, + { label: 'below-start', position: 'below', align: 'start' }, + { label: 'below', position: 'below', align: 'center' }, + { label: 'below-end', position: 'below', align: 'end' }, ]; -export const CoverTarget = (): React.ReactNode => ( -
-
- {cells.map(cell => ( -
- +export const CoverTarget = (): React.ReactNode => { + const [selected, setSelected] = React.useState(options[1]); + + return ( +
+
+ +
+ - + - Container + Container
- ))} +
-
-); + ); +}; diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningDefault.stories.tsx index 39994731f5681..cf486690a55fd 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningDefault.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningDefault.stories.tsx @@ -2,18 +2,14 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; import type { PositioningProps } from '@fluentui/react-headless-components-preview/positioning'; -const classes = { - trigger: - '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', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 min-w-[160px]', -}; +import styles from './positioning.module.css'; export const Default = (props: PositioningProps): React.ReactNode => ( - + - Container + Container ); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFallbackPositions.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFallbackPositions.stories.tsx index be66959f6b0e8..7e2f9d43437d6 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFallbackPositions.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFallbackPositions.stories.tsx @@ -1,42 +1,26 @@ import * as React from 'react'; -import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox'; import { InlineAnchored } from './InlineAnchored'; -const classes = { - page: 'flex flex-col gap-6 p-4', - sectionTitle: 'text-sm font-semibold text-gray-800 mb-1', - sectionNote: 'text-xs text-gray-500 max-w-xl mb-2', - grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4', - trigger: - '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', - surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-3 min-w-[200px] max-w-xs text-sm', -}; +import styles from './positioning.module.css'; export const FallbackPositions = (): React.ReactNode => ( -
- - -
-

Basic fallback

-

+

+
+

Basic fallback

+

Primary above overflows the box → first fallback below-start fits → surface renders there.

-
+
- trigger near top - + } > Requested: above · fallbacks: below-start, after @@ -44,25 +28,23 @@ export const FallbackPositions = (): React.ReactNode => (
-
-

Chain walking

-

+

+

Chain walking

+

Trigger pinned to top-left. Primary above overflows, first fallback before also overflows (no room to the left), so the browser falls through to below. The live{' '} Actual readout should read below.

-
+
- top-left trigger - + } > Requested: above · fallbacks: before, below @@ -70,43 +52,35 @@ export const FallbackPositions = (): React.ReactNode => (
-
-

Custom chain replaces default flip

-

+

+

Custom chain replaces default flip

+

Same overflow condition, different chains. Left popover has no fallbackPositions → default{' '} flip-block, flip-inline fires → surface ends up below. Right popover passes ['after']{' '} → custom chain replaces defaults → surface goes to the right instead of flipping.

-
-
+
+
- default (flip) - + } > Requested: above · no custom fallbacks
-
+
+ } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingBlock.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingBlock.stories.tsx index dd09c15be9abb..ff3b01731f256 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingBlock.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingBlock.stories.tsx @@ -1,48 +1,30 @@ import * as React from 'react'; -import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox'; import { InlineAnchored } from './InlineAnchored'; import descriptionMd from './PositioningFlippingBlockDescription.md'; - -const classes = { - page: 'flex flex-col gap-4 p-4', - grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4', - trigger: - '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', - surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-2 w-[160px] text-xs', -}; +import styles from './positioning.module.css'; export const FlippingBlock = (): React.ReactNode => ( -
- - -
-
+
+
+
- trigger near top - + } > Requested: above → flips below
-
+
+ } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingCorner.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingCorner.stories.tsx index 4325ed1db1cc1..d5f3f1061aaf5 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingCorner.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingCorner.stories.tsx @@ -1,28 +1,18 @@ import * as React from 'react'; -import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox'; import { InlineAnchored } from './InlineAnchored'; import descriptionMd from './PositioningFlippingCornerDescription.md'; - -const classes = { - page: 'flex flex-col gap-4 p-4', - grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4', - trigger: - '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', - surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-3 w-[320px] max-h-[140px] text-sm', -}; +import styles from './positioning.module.css'; export const FlippingCorner = (): React.ReactNode => ( -
- - -
-
+
+
+
+ } @@ -31,12 +21,12 @@ export const FlippingCorner = (): React.ReactNode => (
-
+
+ } @@ -45,12 +35,12 @@ export const FlippingCorner = (): React.ReactNode => (
-
+
+ } @@ -59,12 +49,12 @@ export const FlippingCorner = (): React.ReactNode => (
-
+
+ } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingInline.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingInline.stories.tsx index 97e24ff2b869b..9326fdf3f937c 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingInline.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningFlippingInline.stories.tsx @@ -1,48 +1,30 @@ import * as React from 'react'; -import { demoBoxClass, demoBoxStyle, flipDemoSurfaceCss } from './demoBox'; import { InlineAnchored } from './InlineAnchored'; import descriptionMd from './PositioningFlippingInlineDescription.md'; - -const classes = { - page: 'flex flex-col gap-4 p-4', - grid: 'grid grid-cols-1 sm:grid-cols-2 gap-4', - trigger: - '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', - surface: 'flip-demo bg-white rounded-md shadow-md border border-gray-200 p-2 w-[160px] text-xs', -}; +import styles from './positioning.module.css'; export const FlippingInline = (): React.ReactNode => ( -
- - -
-
+
+
+
- trigger on left - + } > Requested: before → flips after
-
+
+ } diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningMatchTargetSize.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningMatchTargetSize.stories.tsx index 337b98b8a2fb6..dfd2e812a2175 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningMatchTargetSize.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningMatchTargetSize.stories.tsx @@ -2,21 +2,17 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; import descriptionMd from './PositioningMatchTargetSizeDescription.md'; - -const classes = { - wrapper: 'flex flex-col items-start gap-4 m-16', - trigger: - 'w-[350px] 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', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 p-4 box-border', -}; +import styles from './positioning.module.css'; export const MatchTargetSize = (): React.ReactNode => ( -
+
- + - This popover has the same width as its target anchor + + This popover has the same width as its target anchor +
); diff --git a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningOffset.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningOffset.stories.tsx index 3d4c2afe951fe..996a8a0c8d672 100644 --- a/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningOffset.stories.tsx +++ b/packages/react-components/react-headless-components-preview/stories/src/Concepts/Positioning/PositioningOffset.stories.tsx @@ -1,61 +1,51 @@ import * as React from 'react'; import { Popover, PopoverTrigger, PopoverSurface } from '@fluentui/react-headless-components-preview/popover'; -const classes = { - outer: 'w-full overflow-auto', - wrapper: 'flex flex-col items-start gap-6 mx-32 my-16 w-max', - row: 'flex items-center gap-3 text-sm text-gray-700', - input: 'w-20 px-2 py-1 border border-gray-300 rounded', - trigger: - 'inline-flex w-fit 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', - surface: 'bg-white rounded-lg shadow-lg border border-gray-200 px-3 py-2 text-sm w-40', - group: 'flex flex-col items-start gap-2', - label: 'text-xs font-semibold text-gray-500 uppercase tracking-wide', -}; +import styles from './positioning.module.css'; export const Offset = (): React.ReactNode => { const [mainAxis, setMainAxis] = React.useState(10); const [crossAxis, setCrossAxis] = React.useState(0); return ( -
-
-