Skip to content

Commit 9e7bc39

Browse files
committed
feat(react-headless-components-preview): add headless Popover built on native CSS anchor positioning
1 parent 3d2d9bf commit 9e7bc39

48 files changed

Lines changed: 3567 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { baseConfig } from '@fluentui/scripts-cypress';
2+
3+
export default baseConfig;

packages/react-components/react-headless-components-preview/library/etc/react-headless-components-preview.api.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ import type { CheckboxBaseProps } from '@fluentui/react-checkbox';
4444
import { CheckboxBaseState } from '@fluentui/react-checkbox';
4545
import type { CheckboxSlots as CheckboxSlots_2 } from '@fluentui/react-checkbox';
4646
import { ComponentProps } from '@fluentui/react-utilities';
47+
import type { ComponentState } from '@fluentui/react-utilities';
4748
import { ContextSelector } from '@fluentui/react-context-selector';
4849
import type { DividerBaseProps } from '@fluentui/react-divider';
4950
import { DividerBaseState } from '@fluentui/react-divider';
5051
import type { DividerSlots as DividerSlots_2 } from '@fluentui/react-divider';
52+
import type { EventData } from '@fluentui/react-utilities';
53+
import type { EventHandler } from '@fluentui/react-utilities';
5154
import type { FieldBaseProps } from '@fluentui/react-field';
5255
import { FieldBaseState } from '@fluentui/react-field';
5356
import { FieldContextValues } from '@fluentui/react-field';
@@ -99,6 +102,7 @@ import type { SkeletonSlots as SkeletonSlots_2 } from '@fluentui/react-skeleton'
99102
import type { SliderBaseProps } from '@fluentui/react-slider';
100103
import { SliderBaseState } from '@fluentui/react-slider';
101104
import type { SliderSlots as SliderSlots_2 } from '@fluentui/react-slider';
105+
import type { Slot } from '@fluentui/react-utilities';
102106
import type { SpinButtonBaseProps } from '@fluentui/react-spinbutton';
103107
import { SpinButtonBaseState } from '@fluentui/react-spinbutton';
104108
import type { SpinButtonSlots as SpinButtonSlots_2 } from '@fluentui/react-spinbutton';
@@ -193,6 +197,9 @@ export type AccordionState = AccordionBaseState & {
193197
};
194198
};
195199

200+
// @public (undocumented)
201+
export type Alignment = (typeof ALIGNMENTS)[keyof typeof ALIGNMENTS];
202+
196203
// @public
197204
export const Avatar: ForwardRefComponent<AvatarProps>;
198205

@@ -370,6 +377,143 @@ export type LinkState = LinkBaseState & {
370377
};
371378
};
372379

380+
// @public
381+
export type OnOpenChangeData = EventData<string, React_2.SyntheticEvent | Event> & {
382+
open: boolean;
383+
};
384+
385+
// @public
386+
export type OpenPopoverEvents = MouseEvent | TouchEvent | React_2.FocusEvent<HTMLElement> | React_2.KeyboardEvent<HTMLElement> | React_2.MouseEvent<HTMLElement>;
387+
388+
// @public
389+
export const Popover: {
390+
(props: PopoverProps): JSXElement;
391+
displayName: string;
392+
};
393+
394+
// @public
395+
export type PopoverContextValue = Pick<PopoverState, 'open' | 'setOpen' | 'toggleOpen' | 'triggerRef' | 'contentRef' | 'arrowRef' | 'openOnHover' | 'openOnContext' | 'trapFocus' | 'withArrow' | 'inline' | 'mountNode'> & {
396+
positioning: {
397+
targetRef: React_2.RefCallback<HTMLElement>;
398+
containerRef: React_2.RefCallback<HTMLElement>;
399+
arrowRef: React_2.RefCallback<HTMLElement>;
400+
};
401+
};
402+
403+
// @public
404+
export type PopoverProps = {
405+
children: [React_2.ReactElement, React_2.ReactElement] | React_2.ReactElement;
406+
open?: boolean;
407+
defaultOpen?: boolean;
408+
onOpenChange?: EventHandler<OnOpenChangeData>;
409+
openOnHover?: boolean;
410+
openOnContext?: boolean;
411+
mouseLeaveDelay?: number;
412+
positioning?: PositioningShorthand;
413+
withArrow?: boolean;
414+
trapFocus?: boolean;
415+
disableAutoFocus?: boolean;
416+
closeOnScroll?: boolean;
417+
closeOnIframeFocus?: boolean;
418+
inline?: boolean;
419+
mountNode?: HTMLElement | null;
420+
};
421+
422+
// @public
423+
export type PopoverState = Required<Pick<PopoverProps, 'open' | 'inline'>> & Pick<PopoverProps, 'onOpenChange' | 'openOnContext' | 'openOnHover' | 'trapFocus' | 'withArrow' | 'disableAutoFocus' | 'mountNode'> & {
424+
setOpen: (e: OpenPopoverEvents, open: boolean) => void;
425+
toggleOpen: (e: OpenPopoverEvents) => void;
426+
triggerRef: React_2.RefObject<HTMLElement | null>;
427+
contentRef: React_2.RefObject<HTMLElement | null>;
428+
arrowRef: React_2.RefObject<HTMLDivElement | null>;
429+
popoverTrigger: React_2.ReactElement | undefined;
430+
popoverSurface: React_2.ReactElement | undefined;
431+
contextTarget: {
432+
x: number;
433+
y: number;
434+
} | undefined;
435+
setContextTarget: (target: {
436+
x: number;
437+
y: number;
438+
} | undefined) => void;
439+
positioning: PositioningReturn;
440+
};
441+
442+
// @public
443+
export const PopoverSurface: ForwardRefComponent<PopoverSurfaceProps>;
444+
445+
// @public (undocumented)
446+
export type PopoverSurfaceProps = ComponentProps<PopoverSurfaceSlots>;
447+
448+
// @public
449+
export type PopoverSurfaceSlots = {
450+
root: Slot<'div'>;
451+
};
452+
453+
// @public (undocumented)
454+
export type PopoverSurfaceState = ComponentState<PopoverSurfaceSlots> & {
455+
inline: boolean;
456+
withArrow: boolean | undefined;
457+
arrowRef: React_2.RefObject<HTMLDivElement | null>;
458+
mountNode: HTMLElement | null | undefined;
459+
'data-open': string;
460+
};
461+
462+
// @public
463+
export const PopoverTrigger: React_2.FC<PopoverTriggerProps>;
464+
465+
// @public
466+
export type PopoverTriggerProps = {
467+
children: React_2.ReactElement;
468+
disableButtonEnhancement?: boolean;
469+
};
470+
471+
// @public
472+
export type PopoverTriggerState = {
473+
children: React_2.ReactElement | null;
474+
};
475+
476+
// @public (undocumented)
477+
export type Position = (typeof POSITIONS)[keyof typeof POSITIONS];
478+
479+
// @public
480+
export type PositioningImperativeRef = {
481+
setTarget: (target: HTMLElement | null) => void;
482+
updatePosition: () => void;
483+
};
484+
485+
// @public (undocumented)
486+
export type PositioningProps = {
487+
position?: Position;
488+
align?: Alignment;
489+
offset?: number | {
490+
mainAxis?: number;
491+
crossAxis?: number;
492+
};
493+
fallbackPositions?: PositioningShorthandValue[];
494+
coverTarget?: boolean;
495+
autoSize?: boolean | 'width' | 'height';
496+
target?: HTMLElement | React_2.RefObject<HTMLElement | null> | null;
497+
strategy?: 'absolute' | 'fixed';
498+
matchTargetSize?: 'width';
499+
pinned?: boolean;
500+
positioningRef?: React_2.Ref<PositioningImperativeRef>;
501+
overflowBoundary?: HTMLElement | React_2.RefObject<HTMLElement | null> | null;
502+
};
503+
504+
// @public (undocumented)
505+
export type PositioningReturn = {
506+
targetRef: React_2.RefCallback<HTMLElement>;
507+
containerRef: React_2.RefCallback<HTMLElement>;
508+
arrowRef: React_2.RefCallback<HTMLElement>;
509+
};
510+
511+
// @public (undocumented)
512+
export type PositioningShorthand = PositioningProps | PositioningShorthandValue;
513+
514+
// @public (undocumented)
515+
export type PositioningShorthandValue = 'above' | 'above-start' | 'above-end' | 'below' | 'below-start' | 'below-end' | 'before' | 'before-top' | 'before-bottom' | 'after' | 'after-top' | 'after-bottom';
516+
373517
// @public
374518
export const ProgressBar: ForwardRefComponent<ProgressBarProps>;
375519

@@ -509,6 +653,17 @@ export const renderInput: (state: InputBaseState) => JSXElement;
509653
// @public
510654
export const renderLink: (state: LinkBaseState) => JSXElement;
511655

656+
// @public
657+
export const renderPopover: (state: PopoverState, contextValues: {
658+
popover: PopoverContextValue;
659+
}) => React_2.ReactElement;
660+
661+
// @public
662+
export const renderPopoverSurface: (state: PopoverSurfaceState) => JSXElement;
663+
664+
// @public
665+
export const renderPopoverTrigger: (state: PopoverTriggerState) => React_2.ReactElement | null;
666+
512667
// @public
513668
export const renderProgressBar: (state: ProgressBarState) => JSXElement;
514669

@@ -816,6 +971,23 @@ export const useInput: (props: InputProps, ref: React_2.Ref<HTMLInputElement>) =
816971
// @public
817972
export const useLink: (props: LinkProps, ref: React_2.Ref<HTMLElement>) => LinkState;
818973

974+
// @public
975+
export const usePopover: (props: PopoverProps, ref: React_2.Ref<HTMLElement>) => PopoverState;
976+
977+
// @public
978+
export const usePopoverContext: <T>(selector: ContextSelector<PopoverContextValue, T>) => T;
979+
980+
// @public (undocumented)
981+
export const usePopoverContextValues: (state: PopoverState) => {
982+
popover: PopoverContextValue;
983+
};
984+
985+
// @public
986+
export const usePopoverSurface: (props: PopoverSurfaceProps, ref: React_2.Ref<HTMLDivElement>) => PopoverSurfaceState;
987+
988+
// @public
989+
export const usePopoverTrigger: (props: PopoverTriggerProps) => PopoverTriggerState;
990+
819991
// @public
820992
export const useProgressBar: (props: ProgressBarProps, ref: React_2.Ref<HTMLDivElement>) => ProgressBarState;
821993

packages/react-components/react-headless-components-preview/library/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
},
2020
"license": "MIT",
2121
"dependencies": {
22+
"@fluentui/keyboard-keys": "^9.0.8",
2223
"@fluentui/react-accordion": "^9.10.0",
2324
"@fluentui/react-avatar": "^9.10.4",
2425
"@fluentui/react-badge": "^9.5.1",
26+
"@fluentui/react-aria": "^9.17.10",
2527
"@fluentui/react-button": "^9.9.0",
2628
"@fluentui/react-breadcrumb": "^9.4.0",
2729
"@fluentui/react-checkbox": "^9.5.17",
2830
"@fluentui/react-dialog": "^9.17.3",
31+
"@fluentui/react-context-selector": "^9.2.15",
2932
"@fluentui/react-divider": "^9.7.0",
3033
"@fluentui/react-field": "^9.4.16",
3134
"@fluentui/react-image": "^9.4.0",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export { Popover, renderPopover, usePopover, usePopoverContext, usePopoverContextValues } from './components/Popover';
2+
export type {
3+
PopoverProps,
4+
PopoverState,
5+
PopoverContextValue,
6+
OpenPopoverEvents,
7+
OnOpenChangeData,
8+
} from './components/Popover';
9+
10+
export { PopoverTrigger, usePopoverTrigger, renderPopoverTrigger } from './components/Popover';
11+
export type { PopoverTriggerProps, PopoverTriggerState } from './components/Popover';
12+
13+
export { PopoverSurface, usePopoverSurface, renderPopoverSurface } from './components/Popover';
14+
export type { PopoverSurfaceSlots, PopoverSurfaceProps, PopoverSurfaceState } from './components/Popover';

0 commit comments

Comments
 (0)