Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f70229d
feat(react-storybook-addon): add shared SB preview styles
dmytrokirpa Apr 4, 2026
0c8340e
change
dmytrokirpa Apr 4, 2026
98cf730
Merge branch 'microsoft:master' into master
dmytrokirpa Apr 10, 2026
545f61e
Merge branch 'microsoft:master' into master
dmytrokirpa Apr 16, 2026
a1da3c7
Merge branch 'microsoft:master' into master
dmytrokirpa Apr 22, 2026
3f359d5
Merge branch 'microsoft:master' into master
dmytrokirpa Sep 8, 2026
8cc280d
Merge branch 'microsoft:master' into master
dmytrokirpa Sep 14, 2026
63cad2a
Merge branch 'microsoft:master' into master
dmytrokirpa Oct 8, 2026
a92dad5
feat(react-positioning): replace Floating UI with a CSS-anchor-positi…
claude Oct 13, 2026
b585d08
fix(react-positioning): use the strategy option when it is set
claude Oct 13, 2026
9cbd844
Merge branch 'master' into claude/exciting-fermi-h0j6uv
dmytrokirpa Oct 13, 2026
2951aad
fix: resolve positioning migration CI regressions
dmytrokirpa Oct 13, 2026
5142605
Merge remote-tracking branch 'origin/master' into claude/exciting-fer…
dmytrokirpa Oct 20, 2026
3f97279
test(react-positioning): pin browser geometry and lifecycle contracts
dmytrokirpa Oct 20, 2026
746d3ea
fix(react-positioning): align engine with browser contracts
dmytrokirpa Oct 19, 2026
d9b900e
test(react-positioning): compare raster with immutable oracle
dmytrokirpa Oct 20, 2026
d1eed64
fix(react-positioning): preserve integration contracts
dmytrokirpa Oct 20, 2026
da8d3b2
test(react-positioning): isolate native fractional layout quantization
dmytrokirpa Oct 20, 2026
e0124c4
test(react-positioning): retain raster checks for control cases
dmytrokirpa Oct 20, 2026
e55abfe
test(react-positioning): hide fixture scrollbars in older Chromium
dmytrokirpa Oct 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "patch",
"comment": "Avoid creating a virtual popover target when no click position is provided.",
"packageName": "@fluentui/react-charts",
"email": "dmytrokirpa@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "minor",
"comment": "feat(usePositioning): use CSS anchor positioning by default, and the engine of @fluentui/react-positioning when the browser doesn't support it or an option can't be handled by CSS",
"packageName": "@fluentui/react-headless-components-preview",
"email": "kirpadv@gmail.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "patch",
"comment": "Preserve resolved positioning targets, pinned offsets, oversized popup placement, and fixed-position clipping.",
"packageName": "@fluentui/react-positioning",
"email": "dmytrokirpa@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{

@github-actions github-actions Bot Oct 12, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Avatar Converged 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Avatar Converged.badgeMask.normal.chromium.png 4 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 404 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 138 Changed
vr-tests-react-components/Positioning.Positioning end.chromium.png 732 Changed
vr-tests-react-components/ProgressBar converged 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 52 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 104 Changed
vr-tests-web-components/Avatar 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/Avatar. - Dark Mode.normal.chromium_1.png 298 Changed
vr-tests-web-components/MenuList 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-web-components/MenuList. - Dark Mode.normal.chromium.png 498 Changed
vr-tests-web-components/MenuList. - RTL.1st selected.chromium_2.png 39384 Changed
vr-tests/Callout 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Callout.Bottom left edge - RTL.default.chromium.png 2199 Changed
vr-tests/Callout.Left bottom edge.default.chromium.png 3182 Changed
vr-tests/Callout.Rendering callout attached to a rectangle.default.chromium.png 1835 Changed
vr-tests/Coachmark 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/Coachmark.Collapsed.default.chromium.png 159 Changed
vr-tests/react-charting-LineChart 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-LineChart.Multiple.default.chromium.png 192 Changed
vr-tests/react-charting-LineChart.Multiple - Dark Mode.default.chromium.png 181 Changed
vr-tests/react-charting-MultiStackBarChart 4 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests/react-charting-MultiStackBarChart.Basic_Absolute - RTL.default.chromium.png 343 Changed
vr-tests/react-charting-MultiStackBarChart.Basic_PartToWhole.default.chromium.png 359 Changed
vr-tests/react-charting-MultiStackBarChart.Basic_Absolute.default.chromium.png 359 Changed
vr-tests/react-charting-MultiStackBarChart.Basic_PartToWhole - RTL.default.chromium.png 343 Changed

There were 2 duplicate changes discarded. Check the build logs for more information.

"type": "minor",
"comment": "feat: compute positions with a built-in engine that follows the rules of CSS anchor positioning, replacing Floating UI to reduce the bundle size",
"packageName": "@fluentui/react-positioning",
"email": "kirpadv@gmail.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "none",
"comment": "test(react-positioning): pin portable browser geometry and lifecycle contracts with exact engine output",
"packageName": "@fluentui/react-positioning",
"email": "dmytrokirpa@microsoft.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "patch",
"comment": "Replace the positioning runtime with a standalone parity engine and enforce the 15,000-byte minified budget.",
"packageName": "@fluentui/react-positioning",
"email": "dmytrokirpa@microsoft.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ export const ChartPopover: React.FunctionComponent<ChartPopoverProps> = React.fo
HTMLDivElement,
ChartPopoverProps
>((props, forwardedRef) => {
const virtualElement: PositioningVirtualElement = {
getBoundingClientRect: () => ({
top: props.clickPosition!.y,
left: props.clickPosition!.x,
right: props.clickPosition!.x,
bottom: props.clickPosition!.y,
x: props.clickPosition!.x,
y: props.clickPosition!.y,
width: 0,
height: 0,
}),
};
const { clickPosition } = props;
const virtualElement: PositioningVirtualElement | undefined = clickPosition
? {
getBoundingClientRect: () => ({
top: clickPosition.y,
left: clickPosition.x,
right: clickPosition.x,
bottom: clickPosition.y,
x: clickPosition.x,
y: clickPosition.y,
width: 0,
height: 0,
}),
}
: undefined;
const target =
typeof props.positioning === 'object' && 'target' in props.positioning
? props.positioning.target !== null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ exports[`GanttChart interaction and accessibility tests should render custom cal
data-popper-placement="top"
data-tabster="{\\"restorer\\":{\\"type\\":0}}"
role="group"
style="position: absolute; left: 0px; top: 0px; margin: 0px; box-sizing: border-box; max-width: 0px; max-height: -20px; overflow-y: auto; transform: translate(-10px, -40px); --fui-positioning-slide-direction-x: 0px; --fui-positioning-slide-direction-y: 1px;"
style="position: absolute; left: 0px; top: 0px; margin: 0px; box-sizing: border-box; max-width: 0px; max-height: 0px; transform: translate(0px, -20px); --fui-positioning-slide-direction-x: 0px; --fui-positioning-slide-direction-y: 1px;"
>
<div
class="fui-cart__calloutContentRoot"
Expand Down Expand Up @@ -4935,7 +4935,7 @@ exports[`GanttChart rendering and behavior tests should render callout correctly
data-popper-placement="top"
data-tabster="{\\"restorer\\":{\\"type\\":0}}"
role="group"
style="position: absolute; left: 0px; top: 0px; margin: 0px; box-sizing: border-box; max-width: 0px; max-height: -20px; overflow-y: auto; transform: translate(-10px, -40px); --fui-positioning-slide-direction-x: 0px; --fui-positioning-slide-direction-y: 1px;"
style="position: absolute; left: 0px; top: 0px; margin: 0px; box-sizing: border-box; max-width: 0px; max-height: 0px; transform: translate(0px, -20px); --fui-positioning-slide-direction-x: 0px; --fui-positioning-slide-direction-y: 1px;"
>
<div
class="fui-cart__calloutContentRoot"
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { usePositioning } from '@fluentui/react-headless-components-preview/positioning';

console.log(usePositioning);

export default {
name: '@fluentui/react-headless-components-preview/positioning: usePositioning',
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ import type { JSXElement } from '@fluentui/react-utilities';
import { PartitionAvatarGroupItems } from '@fluentui/react-avatar';
import { partitionAvatarGroupItems } from '@fluentui/react-avatar';
import { PartitionAvatarGroupItemsOptions } from '@fluentui/react-avatar';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import type * as React_2 from 'react';
import { renderAvatarGroup_unstable as renderAvatarGroup } from '@fluentui/react-avatar';
import { renderAvatarGroupItem_unstable as renderAvatarGroupItem } from '@fluentui/react-avatar';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { OptionGroupState } from '@fluentui/react-combobox';
import { OptionProps } from '@fluentui/react-combobox';
import { OptionSlots } from '@fluentui/react-combobox';
import type { OptionState as OptionState_2 } from '@fluentui/react-combobox';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import * as React_2 from 'react';
import { renderListbox_unstable as renderListbox } from '@fluentui/react-combobox';
import { renderOption_unstable as renderOption } from '@fluentui/react-combobox';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { OptionGroupState } from '@fluentui/react-combobox';
import { OptionProps } from '@fluentui/react-combobox';
import { OptionSlots } from '@fluentui/react-combobox';
import type { OptionState as OptionState_2 } from '@fluentui/react-combobox';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import type * as React_2 from 'react';
import { renderListbox_unstable as renderListbox } from '@fluentui/react-combobox';
import { renderOption_unstable as renderOption } from '@fluentui/react-combobox';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import type { EventHandler } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { JSXElement } from '@fluentui/react-utilities';
import { LabelBaseProps } from '@fluentui/react-label';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ import { MenuBaseState as MenuState } from '@fluentui/react-menu';
import { MenuTriggerChildProps } from '@fluentui/react-menu';
import { MenuTriggerProps } from '@fluentui/react-menu';
import { MenuTriggerState } from '@fluentui/react-menu';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import * as React_2 from 'react';
import { renderMenuDivider_unstable as renderMenuDivider } from '@fluentui/react-menu';
import { renderMenuGroup_unstable as renderMenuGroup } from '@fluentui/react-menu';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import type { EventHandler } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { JSXElement } from '@fluentui/react-utilities';
import type { PopoverTriggerChildProps as PopoverTriggerChildProps_2 } from '@fluentui/react-popover';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { TriggerProps } from '@fluentui/react-utilities';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
```ts

import { Alignment } from '@fluentui/react-positioning';
import { AutoSize } from '@fluentui/react-positioning';
import { Offset } from '@fluentui/react-positioning';
import { OffsetFunction } from '@fluentui/react-positioning';
import { OffsetFunctionParam } from '@fluentui/react-positioning';
import { OffsetObject } from '@fluentui/react-positioning';
import { OffsetShorthand } from '@fluentui/react-positioning';
import { Position } from '@fluentui/react-positioning';
import { PositioningBoundary } from '@fluentui/react-positioning';
import { PositioningImperativeRef } from '@fluentui/react-positioning';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import { PositioningProps } from '@fluentui/react-positioning';
import { PositioningRect } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningVirtualElement } from '@fluentui/react-positioning';
import type * as React_2 from 'react';
import { SetVirtualMouseTarget } from '@fluentui/react-positioning';

export { Alignment }

Expand All @@ -20,27 +31,44 @@ export const ALIGNMENTS: {
readonly end: "end";
};

export { AutoSize }

// @public
export function getPlacementString(position: Position, align: Alignment): PositioningShorthandValue;

export { Offset }

export { OffsetFunction }

export { OffsetFunctionParam }

export { OffsetObject }

export { OffsetShorthand }

export { Position }

export { PositioningBoundary }

export { PositioningImperativeRef }

// @public (undocumented)
export type PositioningProps = Pick<PositioningProps_2, 'align' | 'coverTarget' | 'fallbackPositions' | 'matchTargetSize' | 'offset' | 'pinned' | 'position' | 'positioningRef' | 'strategy' | 'target'>;
export { PositioningProps }

export { PositioningRect }

// @public (undocumented)
export type PositioningReturn = {
targetRef: React_2.RefCallback<HTMLElement>;
containerRef: React_2.RefCallback<HTMLElement>;
arrowRef: React_2.RefCallback<HTMLElement>;
};

// @public (undocumented)
export type PositioningShorthand = PositioningProps | PositioningShorthandValue;
export { PositioningShorthand }

export { PositioningShorthandValue }

export { PositioningVirtualElement }

// @public (undocumented)
export const POSITIONS: {
readonly above: "above";
Expand All @@ -52,8 +80,15 @@ export const POSITIONS: {
// @public (undocumented)
export const resolvePositioningShorthand: ResolvePositioningShorthand;

// @public (undocumented)
export function usePositioning(options: PositioningProps): PositioningReturn;
export { SetVirtualMouseTarget }

// @public
export function usePositioning(options: UsePositioningOptions): PositioningReturn;

// @public
export type UsePositioningOptions = PositioningProps & {
enabled?: boolean;
};

// (No @packageDocumentation comment for this package)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import type { JSXElement } from '@fluentui/react-utilities';
import { ListboxProps } from '@fluentui/react-combobox';
import { OptionSlots } from '@fluentui/react-combobox';
import type { OptionState as OptionState_2 } from '@fluentui/react-combobox';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import type * as React_2 from 'react';
import { renderTagPickerButton_unstable as renderTagPickerButton } from '@fluentui/react-tag-picker';
import { renderTagPickerControl_unstable as renderTagPickerControl } from '@fluentui/react-tag-picker';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { JSXElement } from '@fluentui/react-utilities';
import type { PopoverContextValue as PopoverContextValue_2 } from '@fluentui/react-popover';
import type { PopoverTriggerChildProps } from '@fluentui/react-popover';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import * as React_2 from 'react';
import { renderTeachingPopoverBody_unstable as renderTeachingPopoverBody } from '@fluentui/react-teaching-popover';
import { renderTeachingPopoverCarouselCard_unstable as renderTeachingPopoverCarouselCard } from '@fluentui/react-teaching-popover';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

import type { JSXElement } from '@fluentui/react-utilities';
import { OnVisibleChangeData } from '@fluentui/react-tooltip';
import type { PositioningProps as PositioningProps_2 } from '@fluentui/react-positioning';
import type { PositioningShorthandValue } from '@fluentui/react-positioning';
import { PositioningShorthand } from '@fluentui/react-positioning';
import type * as React_2 from 'react';
import type { TooltipBaseProps } from '@fluentui/react-tooltip';
import type { TooltipBaseState } from '@fluentui/react-tooltip';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,26 +281,31 @@ describe('Dialog', () => {
});

it('should not close when clicking inside a portaled Popover descendant', () => {
mount(
<Dialog modalType="modal">
<DialogTrigger>
<Button id={dialogTriggerOpenId}>Open dialog</Button>
</DialogTrigger>
<DialogSurface>
<DialogBody>
<DialogTitle>Dialog title</DialogTitle>
<Popover>
<PopoverTrigger disableButtonEnhancement>
<Button id="popover-trigger-btn">Toggle popover</Button>
</PopoverTrigger>
<PopoverSurface aria-label="popover">
<Button id="popover-inner-btn">Inner action</Button>
</PopoverSurface>
</Popover>
</DialogBody>
</DialogSurface>
</Dialog>,
);
const Example = () => {
const [mountNode, setMountNode] = React.useState<HTMLDialogElement | null>(null);
return (
<Dialog modalType="modal">
<DialogTrigger>
<Button id={dialogTriggerOpenId}>Open dialog</Button>
</DialogTrigger>
<DialogSurface ref={setMountNode}>
<DialogBody>
<DialogTitle>Dialog title</DialogTitle>
{/* Legacy portaled surfaces must stay inside the native modal's non-inert subtree. */}
<Popover mountNode={mountNode}>
<PopoverTrigger disableButtonEnhancement>
<Button id="popover-trigger-btn">Toggle popover</Button>
</PopoverTrigger>
<PopoverSurface aria-label="popover">
<Button id="popover-inner-btn">Inner action</Button>
</PopoverSurface>
</Popover>
</DialogBody>
</DialogSurface>
</Dialog>
);
};
mount(<Example />);
cy.get(dialogTriggerOpenSelector).realClick();
cy.get('#popover-trigger-btn').realClick();
cy.get('#popover-inner-btn').should('be.visible').realClick();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ const renderTagPicker = (props: Partial<Pick<TagPickerProps, 'disabled' | 'posit
);
};

// jsdom doesn't implement `CSS.supports()`, these tests cover the CSS anchor positioning path
beforeAll(() => {
Object.defineProperty(globalThis, 'CSS', { value: { supports: () => true }, configurable: true, writable: true });
});

afterAll(() => {
Reflect.deleteProperty(globalThis, 'CSS');
});

describe('TagPicker', () => {
it('renders the input trigger and the options list when open', () => {
const { getByRole, getAllByRole } = renderTagPicker();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
export { usePositioning } from './usePositioning';
export type {
Position,
Alignment,
AutoSize,
Offset,
OffsetFunction,
OffsetFunctionParam,
OffsetObject,
OffsetShorthand,
Position,
PositioningBoundary,
PositioningImperativeRef,
PositioningProps,
PositioningRect,
PositioningReturn,
PositioningImperativeRef,
PositioningShorthand,
PositioningShorthandValue,
PositioningVirtualElement,
SetVirtualMouseTarget,
UsePositioningOptions,
} from './usePositioning';
export { POSITIONS, ALIGNMENTS, getPlacementString, resolvePositioningShorthand } from './usePositioning';
Loading
Loading