From 866c57491fa1ed144926b26007167ca47edd555f Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Mon, 9 Mar 2026 11:38:50 +1100 Subject: [PATCH 1/9] feat: S2 ListView HCM --- packages/@react-spectrum/s2/src/ListView.tsx | 58 +++++++++++++++---- packages/@react-spectrum/s2/src/TableView.tsx | 5 +- .../s2/stories/ListView.stories.tsx | 3 +- .../s2/style/__tests__/style-macro.test.js | 17 ++++++ .../@react-spectrum/s2/style/style-macro.ts | 15 +++-- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/packages/@react-spectrum/s2/src/ListView.tsx b/packages/@react-spectrum/s2/src/ListView.tsx index b749a5a4404..ebe5f3d3fab 100644 --- a/packages/@react-spectrum/s2/src/ListView.tsx +++ b/packages/@react-spectrum/s2/src/ListView.tsx @@ -289,9 +289,15 @@ const listitem = style({ ...focusRing(), - outlineOffset: -2, + outlineOffset: { + default: -2, + forcedColors: -3 + }, + outlineWidth: { + default: 2, + forcedColors: '[3px]' + }, + outlineColor: { + default: 'focus-ring', + forcedColors: { + default: 'Highlight', + selectionStyle: { + highlight: 'ButtonBorder' + } + } + }, position: 'absolute', inset: 0, top: { @@ -537,7 +572,8 @@ let listRowFocusRing = style({ isQuiet: 'default' }, zIndex: 1, - pointerEvents: 'none' + pointerEvents: 'none', + }); export let label = style({ @@ -770,7 +806,7 @@ export function ListViewItem(props: ListViewItemProps): ReactNode { isLastItem: isLastItem(id, state) }) } /> - {renderProps.isFocusVisible && + {renderProps.isFocusVisible &&
= { }, tags: ['autodocs'], argTypes: { - ...categorizeArgTypes('Events', ['onSelectionChange']) + ...categorizeArgTypes('Events', ['onSelectionChange']), + children: {table: {disable: true}} }, title: 'ListView', args: { diff --git a/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js b/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js index 79efca76d19..acf21462e48 100644 --- a/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js +++ b/packages/@react-spectrum/s2/style/__tests__/style-macro.test.js @@ -366,6 +366,23 @@ describe('style-macro', () => { expect(js({isSelected: true})).toMatchInlineSnapshot('" ple12 -macro-dynamic-37zkvn"'); }); + it('inherits parent default when nested branch has no default key', () => { + let {css, js} = testStyle({ + color: { + forcedColors: { + default: 'ButtonText', + variant: { + highlight: {isSelected: 'HighlightText'} + } + } + } + }); + // forcedColors.default should apply when variant=highlight but !isSelected + expect(css).toContain('ButtonText'); + expect(js({variant: 'highlight'})).toMatchInlineSnapshot('" plb12 -macro-dynamic-1owjb9s"'); + expect(js({variant: 'highlight', isSelected: true})).toMatchInlineSnapshot('" ple12 -macro-dynamic-37zkvn"'); + }); + it('should expand shorthand properties to longhands', () => { let {js, css} = testStyle({ padding: 24 diff --git a/packages/@react-spectrum/s2/style/style-macro.ts b/packages/@react-spectrum/s2/style/style-macro.ts index 8286f4dfa8a..c8e604f0fe5 100644 --- a/packages/@react-spectrum/s2/style/style-macro.ts +++ b/packages/@react-spectrum/s2/style/style-macro.ts @@ -223,7 +223,7 @@ export function createTheme(theme: T): StyleFunction(theme: T): StyleFunction Date: Mon, 9 Mar 2026 11:54:41 +1100 Subject: [PATCH 2/9] fix lint --- packages/@react-spectrum/s2/src/ListView.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/@react-spectrum/s2/src/ListView.tsx b/packages/@react-spectrum/s2/src/ListView.tsx index ebe5f3d3fab..5fb3390708f 100644 --- a/packages/@react-spectrum/s2/src/ListView.tsx +++ b/packages/@react-spectrum/s2/src/ListView.tsx @@ -530,7 +530,11 @@ const listRowBackground = style({ ...focusRing(), @@ -572,8 +576,7 @@ let listRowFocusRing = style Date: Mon, 9 Mar 2026 12:18:39 +1100 Subject: [PATCH 3/9] Fix pre-existing issue in Menu --- packages/@react-spectrum/s2/src/Menu.tsx | 31 ++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index 54e3772f1ee..bdbe7548031 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -169,13 +169,11 @@ export let menuitem = style & }, color: { default: baseColor('neutral'), + isDisabled: 'disabled', forcedColors: { default: 'ButtonText', - isFocused: 'HighlightText' - }, - isDisabled: { - default: 'disabled', - forcedColors: 'GrayText' + isFocused: 'HighlightText', + isDisabled: 'GrayText' } }, position: 'relative', @@ -278,7 +276,7 @@ export let label = style<{size: string}>({ marginTop: '--labelPadding' }); -export let description = style({ +export let description = style<{size: 'S' | 'M' | 'L' | 'XL', isFocused: boolean, isDisabled: boolean}>({ gridArea: 'description', font: { default: 'ui-sm', @@ -294,7 +292,12 @@ export let description = style({ // Ideally this would use the same token as hover, but we don't have access to that here. // TODO: should we always consider isHovered and isFocused to be the same thing? isFocused: 'gray-800', - isDisabled: 'disabled' + isDisabled: 'disabled', + forcedColors: { + default: 'ButtonText', + isFocused: 'HighlightText', + isDisabled: 'GrayText' + } }, transition: 'default' }); @@ -304,7 +307,7 @@ let value = style({ marginStart: 8 }); -let keyboard = style<{size: 'S' | 'M' | 'L' | 'XL', isDisabled: boolean}>({ +let keyboard = style<{size: 'S' | 'M' | 'L' | 'XL', isDisabled: boolean, isFocused: boolean}>({ gridArea: 'keyboard', marginStart: 8, font: 'ui', @@ -313,6 +316,8 @@ let keyboard = style<{size: 'S' | 'M' | 'L' | 'XL', isDisabled: boolean}>({ default: 'gray-600', isDisabled: 'disabled', forcedColors: { + default: 'ButtonText', + isFocused: 'HighlightText', isDisabled: 'GrayText' } }, @@ -384,11 +389,6 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu { let {children} = props; let checkboxRenderProps = {...renderProps, size, isFocused: false, isFocusVisible: false, isIndeterminate: false, isReadOnly: false, isInvalid: false, isRequired: false}; + let isFocused = (renderProps.hasSubmenu && renderProps.isOpen) || renderProps.isFocused; return ( <> {renderProps.selectionMode === 'single' && !renderProps.hasSubmenu && } From 8415194fa926f7926e7efd6df301c14f3e07e6ec Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Mon, 9 Mar 2026 12:25:06 +1100 Subject: [PATCH 4/9] Fix typescript --- packages/@react-spectrum/s2/src/ComboBox.tsx | 5 ----- packages/@react-spectrum/s2/src/Picker.tsx | 5 ----- packages/@react-spectrum/s2/src/TabsPicker.tsx | 6 ------ 3 files changed, 16 deletions(-) diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 32bbeb3feb8..26129e5032c 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -691,11 +691,6 @@ const ComboboxInner = forwardRef(function ComboboxInner(props: ComboBoxProps (props: PickerProps, ref: FocusableRef Date: Mon, 9 Mar 2026 14:06:51 +1100 Subject: [PATCH 5/9] fix Section headers --- packages/@react-spectrum/s2/src/ComboBox.tsx | 5 +++++ packages/@react-spectrum/s2/src/Picker.tsx | 5 +++++ packages/@react-spectrum/s2/src/TabsPicker.tsx | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 26129e5032c..87509f9c5d9 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -691,6 +691,11 @@ const ComboboxInner = forwardRef(function ComboboxInner(props: ComboBoxProps (props: PickerProps, ref: FocusableRef Date: Mon, 9 Mar 2026 14:28:19 +1100 Subject: [PATCH 6/9] fix bad description color --- packages/@react-spectrum/s2/src/Menu.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index bdbe7548031..e4af9a6d5a3 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -295,7 +295,6 @@ export let description = style<{size: 'S' | 'M' | 'L' | 'XL', isFocused: boolean isDisabled: 'disabled', forcedColors: { default: 'ButtonText', - isFocused: 'HighlightText', isDisabled: 'GrayText' } }, From a98458ff770ea3321b6d498078c04756fcc99f73 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Mon, 9 Mar 2026 14:40:49 +1100 Subject: [PATCH 7/9] more intuitive control for font color of description --- packages/@react-spectrum/s2/src/Menu.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index e4af9a6d5a3..72f44274b6f 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -294,8 +294,7 @@ export let description = style<{size: 'S' | 'M' | 'L' | 'XL', isFocused: boolean isFocused: 'gray-800', isDisabled: 'disabled', forcedColors: { - default: 'ButtonText', - isDisabled: 'GrayText' + default: 'inherit' } }, transition: 'default' @@ -315,9 +314,7 @@ let keyboard = style<{size: 'S' | 'M' | 'L' | 'XL', isDisabled: boolean, isFocus default: 'gray-600', isDisabled: 'disabled', forcedColors: { - default: 'ButtonText', - isFocused: 'HighlightText', - isDisabled: 'GrayText' + default: 'inherit' } }, unicodeBidi: 'plaintext' From 0d42e3b77c4c61610c2a33f4e4c8010fcc28f4aa Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Mon, 9 Mar 2026 14:46:31 +1100 Subject: [PATCH 8/9] dont' forget menu --- packages/@react-spectrum/s2/src/Menu.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index 72f44274b6f..ea1731cffc9 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -385,6 +385,11 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu Date: Tue, 10 Mar 2026 16:08:41 +1100 Subject: [PATCH 9/9] fix description in ListView HCM --- packages/@react-spectrum/s2/src/ListView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@react-spectrum/s2/src/ListView.tsx b/packages/@react-spectrum/s2/src/ListView.tsx index 5fb3390708f..6a201f6a84d 100644 --- a/packages/@react-spectrum/s2/src/ListView.tsx +++ b/packages/@react-spectrum/s2/src/ListView.tsx @@ -606,7 +606,8 @@ export let description = style({ font: 'ui-sm', color: { default: baseColor('neutral-subdued'), - isDisabled: 'disabled' + isDisabled: 'disabled', + forcedColors: 'inherit' }, transition: 'default' });