From 82ad0b7ae91aab796fe8a5dbf5b07d17aa415042 Mon Sep 17 00:00:00 2001 From: Ray Knight <5431637+ArrayKnight@users.noreply.github.com> Date: Wed, 2 Sep 2026 04:50:36 -0700 Subject: [PATCH 1/2] fix(react-color-picker): use nullish fallback in adjustChannel so zero channel values resolve (#36664) Co-authored-by: Claude Fable 5 --- ...icker-23cd9249-19ec-4f83-b918-1fbfa29c5119.json | 7 +++++++ .../library/src/utils/adjustChannel.test.ts | 14 ++++++++++++++ .../library/src/utils/adjustChannel.ts | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 change/@fluentui-react-color-picker-23cd9249-19ec-4f83-b918-1fbfa29c5119.json diff --git a/change/@fluentui-react-color-picker-23cd9249-19ec-4f83-b918-1fbfa29c5119.json b/change/@fluentui-react-color-picker-23cd9249-19ec-4f83-b918-1fbfa29c5119.json new file mode 100644 index 00000000000000..d3c8cd4ef25ddd --- /dev/null +++ b/change/@fluentui-react-color-picker-23cd9249-19ec-4f83-b918-1fbfa29c5119.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: use nullish fallback in adjustChannel so a channel whose value is 0 resolves to its own action instead of hue's", + "packageName": "@fluentui/react-color-picker", + "email": "array.knight@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-color-picker/library/src/utils/adjustChannel.test.ts b/packages/react-components/react-color-picker/library/src/utils/adjustChannel.test.ts index 22f357d92c8ccb..39a2e5a119aa22 100644 --- a/packages/react-components/react-color-picker/library/src/utils/adjustChannel.test.ts +++ b/packages/react-components/react-color-picker/library/src/utils/adjustChannel.test.ts @@ -38,4 +38,18 @@ describe('adjustChannel', () => { expect(adjustChannel('saturation', actions)).toBe('saturationAction'); expect(adjustChannel('value', actions)).toBe('valueAction'); }); + + // Regression test for https://github.com/microsoft/fluentui/issues/36646 — the previous `||` + // fallback treated falsy channel values (e.g. 0) as missing and returned the hue action instead. + it('should return falsy channel values instead of falling back to the hue action', () => { + const numericActions: ChannelActions = { + hue: 120, + saturation: 0, + value: 0, + }; + + expect(adjustChannel('saturation', numericActions)).toBe(0); + expect(adjustChannel('value', numericActions)).toBe(0); + expect(adjustChannel('hue', numericActions)).toBe(120); + }); }); diff --git a/packages/react-components/react-color-picker/library/src/utils/adjustChannel.ts b/packages/react-components/react-color-picker/library/src/utils/adjustChannel.ts index 97b91626d17f56..39e8bdbf2c3937 100644 --- a/packages/react-components/react-color-picker/library/src/utils/adjustChannel.ts +++ b/packages/react-components/react-color-picker/library/src/utils/adjustChannel.ts @@ -29,5 +29,5 @@ export type ChannelActions = { * @returns {T} - The result of the action corresponding to the specified channel, or the hue action if the channel is not found. */ export function adjustChannel(channel: ColorChannel, actions: ChannelActions): T { - return actions[channel] || actions.hue; + return actions[channel] ?? actions.hue; } From a49a072dcb2aebf68d1f03fbddcd9781539a5459 Mon Sep 17 00:00:00 2001 From: Ray Knight <5431637+ArrayKnight@users.noreply.github.com> Date: Wed, 2 Sep 2026 04:51:25 -0700 Subject: [PATCH 2/2] fix(react-headless-components-preview): apply state.arrowClassName in renderTooltip (#36668) Co-authored-by: Claude Fable 5 --- ...-bebba5c3-9ce1-42db-b357-0a5b55eec41e.json | 7 ++++++ .../src/components/Tooltip/Tooltip.test.tsx | 23 +++++++++++++++++++ .../src/components/Tooltip/renderTooltip.tsx | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 change/@fluentui-react-headless-components-preview-bebba5c3-9ce1-42db-b357-0a5b55eec41e.json diff --git a/change/@fluentui-react-headless-components-preview-bebba5c3-9ce1-42db-b357-0a5b55eec41e.json b/change/@fluentui-react-headless-components-preview-bebba5c3-9ce1-42db-b357-0a5b55eec41e.json new file mode 100644 index 00000000000000..1e0c543d753db9 --- /dev/null +++ b/change/@fluentui-react-headless-components-preview-bebba5c3-9ce1-42db-b357-0a5b55eec41e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: renderTooltip applies state.arrowClassName to the arrow element instead of silently discarding it", + "packageName": "@fluentui/react-headless-components-preview", + "email": "array.knight@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/Tooltip.test.tsx b/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/Tooltip.test.tsx index 4e30e425969ed8..6badba098e9beb 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/Tooltip.test.tsx +++ b/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/Tooltip.test.tsx @@ -4,6 +4,9 @@ import { resetIdsForTests } from '@fluentui/react-utilities'; import { isConformant } from '../../testing/isConformant'; import type { IsConformantOptions } from '@fluentui/react-conformance'; import { Tooltip } from './Tooltip'; +import { useTooltip } from './useTooltip'; +import { renderTooltip } from './renderTooltip'; +import type { TooltipProps } from './Tooltip.types'; export const getTooltipElement: IsConformantOptions['getTargetElement'] = () => { return screen.queryByRole('tooltip') as HTMLElement; @@ -111,6 +114,26 @@ describe('Tooltip', () => { expect(screen.getByRole('tooltip').querySelector('[data-arrow]')).not.toBeNull(); }); + // Regression test for https://github.com/microsoft/fluentui/issues/36650 — renderTooltip dropped + // state.arrowClassName, so a styled layer setting it between useTooltip and renderTooltip never + // reached the arrow element. + it('applies state.arrowClassName to the arrow element', () => { + const StyledTooltip = (props: TooltipProps) => { + const state = useTooltip(props); + state.arrowClassName = 'custom-arrow-class'; + return renderTooltip(state); + }; + + render( + + + , + ); + + const arrow = screen.getByRole('tooltip').querySelector('[data-arrow]'); + expect(arrow).toHaveClass('custom-arrow-class'); + }); + it('does not render arrow element when withArrow is false', () => { render( diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/renderTooltip.tsx b/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/renderTooltip.tsx index 4e8d795e2ae94c..8d4047657389c4 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/renderTooltip.tsx +++ b/packages/react-components/react-headless-components-preview/library/src/components/Tooltip/renderTooltip.tsx @@ -16,7 +16,7 @@ export const renderTooltip = (state: TooltipState): JSXElement => { {state.children} {state.shouldRenderTooltip && ( - {state.withArrow &&
} + {state.withArrow &&
} {state.content.children} )}