From 9b5f409c5750c43db657367b833eccad070e9a80 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Wed, 22 Apr 2026 13:07:26 -0700 Subject: [PATCH 1/7] chore: Deploy to AWS via OIDC (#9960) --- .circleci/config.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d2cdca6d379..17cd2821db3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,10 +63,17 @@ commands: - run: name: Check AWS credentials command: | - if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ]; then + if [ -z "$AWS_ROLE_ARN" ]; then echo "AWS credentials not found" circleci-agent step halt fi + - aws-cli/setup: + role_arn: $AWS_ROLE_ARN + region: $AWS_DEFAULT_REGION + role_session_name: "CircleCI-Deploy-Session" + - run: + name: Verify AWS CLI setup + command: aws sts get-caller-identity - run: name: Configure AWS CLI for concurrent requests command: aws configure set default.s3.max_concurrent_requests 100 From 75fc5a2dd0735609285a62cb6dc6890b6ddd9c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikolas=20Schr=C3=B6ter?= <25958801+nwidynski@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:52:17 +0200 Subject: [PATCH 2/7] fix: compatibility for modern icu (#9887) * feat: compatibility for modern icu * update node version so that we can have correct instructions in our contributing and the nvmrc will work if you have 2.14.1 or above installed already * Apply suggestions from code review Co-authored-by: Robert Snow --------- Co-authored-by: Rob Snow Co-authored-by: Robert Snow --- .circleci/config.yml | 8 ++++---- CONTRIBUTING.md | 2 +- .../react-spectrum/test/datepicker/DatePicker.test.js | 4 ++-- packages/@internationalized/number/src/NumberParser.ts | 5 +++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17cd2821db3..616ec0804f0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,14 +20,14 @@ orbs: executors: rsp: docker: - - image: cimg/node:24.13.0 + - image: cimg/node:24.14.1 environment: CACHE_VERSION: v1 working_directory: ~/react-spectrum rsp-large: docker: - - image: cimg/node:24.13.0 + - image: cimg/node:24.14.1 resource_class: large environment: CACHE_VERSION: v1 @@ -35,7 +35,7 @@ executors: rsp-xlarge: docker: - - image: cimg/node:24.13.0 + - image: cimg/node:24.14.1 resource_class: xlarge environment: CACHE_VERSION: v1 @@ -43,7 +43,7 @@ executors: rsp-2xlarge: docker: - - image: cimg/node:24.13.0 + - image: cimg/node:24.14.1 resource_class: 2xlarge environment: CACHE_VERSION: v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4bada46eab9..d9586b844dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,7 @@ If you are looking for place to start, consider the following options: ## Developing When you are ready to start developing you can clone the repo and start storybook. -Make sure you have the following requirements installed: [node](https://nodejs.org/) (v24.13.0+) and [yarn](https://yarnpkg.com/en/) (v1.22.0+) +Make sure you have the following requirements installed: [node](https://nodejs.org/) (v24.14.1+) and [yarn](https://yarnpkg.com/en/) (v1.22.0+) Fork the repo first using [this guide](https://help.github.com/articles/fork-a-repo), then clone it locally. ``` diff --git a/packages/@adobe/react-spectrum/test/datepicker/DatePicker.test.js b/packages/@adobe/react-spectrum/test/datepicker/DatePicker.test.js index bfb348ecb03..f2c23dbb842 100644 --- a/packages/@adobe/react-spectrum/test/datepicker/DatePicker.test.js +++ b/packages/@adobe/react-spectrum/test/datepicker/DatePicker.test.js @@ -1621,8 +1621,8 @@ describe('DatePicker', function () { testInput('era,', new CalendarDate(new JapaneseCalendar(), 'reiwa', 5, 2, 3), 'h', new CalendarDate(new JapaneseCalendar(), 'heisei', 5, 2, 3), false, {locale: 'en-US-u-ca-japanese'}); testInput('era,', new CalendarDate(new JapaneseCalendar(), 'reiwa', 5, 2, 3), 's', new CalendarDate(new JapaneseCalendar(), 'showa', 5, 2, 3), false, {locale: 'en-US-u-ca-japanese'}); testInput('era,', new CalendarDate(new JapaneseCalendar(), 'showa', 5, 2, 3), 'r', new CalendarDate(new JapaneseCalendar(), 'reiwa', 5, 2, 3), false, {locale: 'en-US-u-ca-japanese'}); - testInput('era,', new CalendarDate(new EthiopicCalendar(), 'AM', 2012, 2, 3), '0', new CalendarDate(new EthiopicCalendar(), 'AA', 2012, 2, 3), false, {locale: 'en-US-u-ca-ethiopic'}); - testInput('era,', new CalendarDate(new EthiopicCalendar(), 'AA', 2012, 2, 3), '1', new CalendarDate(new EthiopicCalendar(), 'AM', 2012, 2, 3), false, {locale: 'en-US-u-ca-ethiopic'}); + testInput('era,', new CalendarDate(new EthiopicCalendar(), 'AM', 2012, 2, 3), 'A', new CalendarDate(new EthiopicCalendar(), 'AA', 2012, 2, 3), false, {locale: 'en-US-u-ca-ethiopic'}); + testInput('era,', new CalendarDate(new EthiopicCalendar(), 'AA', 2012, 2, 3), 'M', new CalendarDate(new EthiopicCalendar(), 'AM', 2012, 2, 3), false, {locale: 'en-US-u-ca-ethiopic'}); }); it('should allow entering invalid dates, and constrain on blur', async function () { diff --git a/packages/@internationalized/number/src/NumberParser.ts b/packages/@internationalized/number/src/NumberParser.ts index 6311f920787..7c93c4f9dd8 100644 --- a/packages/@internationalized/number/src/NumberParser.ts +++ b/packages/@internationalized/number/src/NumberParser.ts @@ -232,6 +232,11 @@ class NumberParserImpl { value = replaceAll(value, "'", this.symbols.group); } + // On newer ICU versions, the special single quote has been normalized, so we need to backport. + if (this.symbols.group === "'" && value.includes('’') && isGroupSymbolAllowed) { + value = replaceAll(value, '’', this.symbols.group); + } + // fr-FR group character is narrow non-breaking space, char code 8239 (U+202F), but that's not a key on the french keyboard, // so allow space and non-breaking space as a group char as well if (this.options.locale === 'fr-FR' && this.symbols.group && isGroupSymbolAllowed) { From 4580bd351294cf658db18d03be933d677501e889 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Thu, 23 Apr 2026 08:32:08 +1000 Subject: [PATCH 3/7] chore: updating contributing guide (#9962) --- CONTRIBUTING.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9586b844dd..962c7e04584 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,7 @@ If you are looking for place to start, consider the following options: ## Developing When you are ready to start developing you can clone the repo and start storybook. + Make sure you have the following requirements installed: [node](https://nodejs.org/) (v24.14.1+) and [yarn](https://yarnpkg.com/en/) (v1.22.0+) Fork the repo first using [this guide](https://help.github.com/articles/fork-a-repo), then clone it locally. @@ -74,7 +75,7 @@ yarn start Or run the documentation and browse to [http://localhost:1234/](http://localhost:1234/) with: ```bash -yarn start:docs +yarn start:s2-docs ``` ### Tests @@ -90,13 +91,13 @@ We use [jest](https://jestjs.io/) for unit tests and [react-testing-library](htt You can run the tests with: ```bash -yarn jest +yarn test ``` You can also get a code coverage report by running: ```bash -yarn jest --coverage +yarn test --coverage ``` ### Linting @@ -118,12 +119,18 @@ yarn start ``` Then, open [http://localhost:9003](http://localhost:9003) in your browser to play around with the components and test your changes. +For S2 Storybook, run: +```bash +yarn start:s2 +``` +Then, open [http://localhost:6006](http://localhost:6006). + ### Documentation Our documentation should always remain up to date. When making changes to components, make sure the appropriate documentation has been updated to reflect those changes. Documentation for each component can be found in the docs folder within a component's package. Other documentation pages can be found in the [packages/dev/docs](https://github.com/adobe/react-spectrum/tree/main/packages/dev/docs) folder in the codebase. Documentation can be run locally by using ```bash -yarn start:docs +yarn start:s2-docs ``` Then, open [http://localhost:1234](http://localhost:1234) in your browser. From 90ce3fc342acae930d526a6621357c2d22bf058b Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Thu, 23 Apr 2026 09:07:38 +1000 Subject: [PATCH 4/7] chore: align TreeView and TableView stories onAction (#9963) --- packages/@react-spectrum/s2/stories/TableView.stories.tsx | 4 ++-- packages/@react-spectrum/s2/stories/TreeView.stories.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/@react-spectrum/s2/stories/TableView.stories.tsx b/packages/@react-spectrum/s2/stories/TableView.stories.tsx index 45ce91272e3..3ac2f3b5d8c 100644 --- a/packages/@react-spectrum/s2/stories/TableView.stories.tsx +++ b/packages/@react-spectrum/s2/stories/TableView.stories.tsx @@ -49,7 +49,7 @@ import User from '../s2wf-icons/S2_Icon_User_20_N.svg'; import {useTreeData} from 'react-stately/useTreeData'; let onActionFunc = action('onAction'); -let noOnAction = null; +let noOnAction = undefined; const onActionOptions = {onActionFunc, noOnAction}; const events = ['onResizeStart', 'onResize', 'onResizeEnd', 'onSelectionChange', 'onSortChange']; @@ -63,7 +63,7 @@ const meta: Meta = { tags: ['autodocs'], args: {...getActionArgs(events)}, argTypes: { - ...categorizeArgTypes('Events', ['onAction', 'onLoadMore', 'onResizeStart', 'onResize', 'onResizeEnd', 'onSelectionChange', 'onSortChange']), + ...categorizeArgTypes('Events', ['onAction', 'onLoadMore', ...events]), children: {table: {disable: true}}, onAction: { options: Object.keys(onActionOptions), // An array of serializable values diff --git a/packages/@react-spectrum/s2/stories/TreeView.stories.tsx b/packages/@react-spectrum/s2/stories/TreeView.stories.tsx index b9bebd80f52..ed9aa94269c 100644 --- a/packages/@react-spectrum/s2/stories/TreeView.stories.tsx +++ b/packages/@react-spectrum/s2/stories/TreeView.stories.tsx @@ -45,9 +45,9 @@ import {useAsyncList} from 'react-stately/useAsyncList'; import {useListData} from 'react-stately/useListData'; let onActionFunc = action('onAction'); -let noOnAction = null; +let noOnAction = undefined; const onActionOptions = {onActionFunc, noOnAction}; -const events = ['onSelectionChange', 'onAction']; +const events = ['onSelectionChange']; const meta: Meta = { component: TreeView, @@ -57,7 +57,7 @@ const meta: Meta = { tags: ['autodocs'], args: {...getActionArgs(events)}, argTypes: { - ...categorizeArgTypes('Events', events), + ...categorizeArgTypes('Events', ['onAction', ...events]), children: {table: {disable: true}}, onAction: { options: Object.keys(onActionOptions), // An array of serializable values From dae986e66d749f2e2300fddb9da130db047fd9ba Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Wed, 22 Apr 2026 16:14:18 -0700 Subject: [PATCH 5/7] fix: ensure Virtualized Table loader width is up to date with table width (#9961) * fix: ensure virtualized Table loader width is updated if table width changes * fix weird scroll behavior when multiple pages loaded * typo --- .../stories/Table.stories.tsx | 67 +++++++++++++++++++ .../react-stately/src/layout/TableLayout.ts | 11 +++ 2 files changed, 78 insertions(+) diff --git a/packages/react-aria-components/stories/Table.stories.tsx b/packages/react-aria-components/stories/Table.stories.tsx index 03f353256db..5409b9d9d5e 100644 --- a/packages/react-aria-components/stories/Table.stories.tsx +++ b/packages/react-aria-components/stories/Table.stories.tsx @@ -1152,6 +1152,73 @@ export const OnLoadMoreTableVirtualizedResizeWrapperStory: StoryObj { + let list = useAsyncList({ + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + await new Promise(resolve => setTimeout(resolve, args.delay)); + } + let res = await fetch(cursor || 'https://swapi.py4e.com/api/people/?search=', {signal}); + let json = await res.json(); + return { + items: json.results, + cursor: json.next + }; + } + }); + + return ( +
+ + + + Name + Height + Mass + Birth Year + + renderEmptyLoader({isLoading: list.loadingState === 'loading'})}> + + {(item) => ( + + {item.name} + {item.height} + {item.mass} + {item.birth_year} + + )} + + + + + +
+
+
+ ); +}; + +export const VirtualizedTableLoaderWidthTestStory: StoryObj = { + render: VirtualizedTableLoaderWidthTest, + name: 'virtualized table, loader dynamic width', + args: {delay: 10000}, + parameters: { + description: { + data: 'resizing the table should also resize the loader element width' + } + } +}; + interface Launch { id: number, mission_name: string, diff --git a/packages/react-stately/src/layout/TableLayout.ts b/packages/react-stately/src/layout/TableLayout.ts index a9fc643b8ad..dca6d1fdfba 100644 --- a/packages/react-stately/src/layout/TableLayout.ts +++ b/packages/react-stately/src/layout/TableLayout.ts @@ -361,6 +361,17 @@ export class TableLayout exten }; } + protected buildLoader(node: GridNode, x: number, y: number): LayoutNode { + let layoutNode = super.buildLoader(node, x, y); + let collection = this.virtualizer!.collection as TableCollection; + + // use the same approach as buildRow to get the proper width of the loader, otherwise + // we get a outdated loader width + layoutNode.layoutInfo.rect.width = this.layoutNodes.get(collection.head?.key ?? 'header')!.layoutInfo.rect.width; + layoutNode.validRect = layoutNode.layoutInfo.rect.intersection(this.requestedRect); + return layoutNode; + } + protected buildNode(node: GridNode, x: number, y: number): LayoutNode { switch (node.type) { case 'headerrow': From e05b106e3a90196885b2299119e5a51b176ca3a3 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Thu, 23 Apr 2026 09:41:26 +1000 Subject: [PATCH 6/7] chore: disable tsdiff on mergequeue (#9964) --- .circleci/config.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 616ec0804f0..48d45555b3d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -940,20 +940,26 @@ workflows: - install filters: branches: - ignore: main + ignore: + - /main$/ + - /gh-readonly-queue\/.*$/ - ts-build-branch: requires: - install filters: branches: - ignore: main + ignore: + - /main$/ + - /gh-readonly-queue\/.*$/ - ts-diff: requires: - ts-build-fork-point - ts-build-branch filters: branches: - ignore: main + ignore: + - /main$/ + - /gh-readonly-queue\/.*$/ - typecheck-docs: requires: - install From f23d22f680a41c8877e6b196dc27ba9947835936 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Wed, 22 Apr 2026 16:57:39 -0700 Subject: [PATCH 7/7] feat: Support help text in Checkbox, Radio, and Switch (#9877) * feat: Support help text in Checkbox, Radio, and Switch * fix * Fix checkbox styling when nested in collections * A couple more tests * fix aria-describedby behavior * Add tests for CheckboxField in collections * Add help text support in S2 * updates from testing * lint * make radiogroup rac example also grey out help text when disabled * whoops forgot it for switch too --------- Co-authored-by: Daniel Lu --- .../react-spectrum/src/checkbox/Checkbox.tsx | 3 +- .../@adobe/react-spectrum/src/radio/Radio.tsx | 3 +- .../react-spectrum/src/switch/Switch.tsx | 3 +- packages/@react-spectrum/s2/src/Checkbox.tsx | 154 ++++++++----- .../@react-spectrum/s2/src/CheckboxGroup.tsx | 7 +- packages/@react-spectrum/s2/src/Field.tsx | 27 ++- .../@react-spectrum/s2/src/RadioGroup.tsx | 101 ++++++--- packages/@react-spectrum/s2/src/Switch.tsx | 98 ++++++--- packages/@react-spectrum/s2/src/useDOMRef.ts | 15 +- .../s2/stories/Checkbox.stories.tsx | 15 ++ .../s2/stories/CheckboxGroup.stories.tsx | 17 ++ .../s2/stories/RadioGroup.stories.tsx | 13 +- .../s2/stories/Switch.stories.tsx | 15 ++ packages/@react-types/shared/src/dom.d.ts | 3 + packages/@react-types/shared/src/refs.d.ts | 2 +- .../dev/s2-docs/pages/react-aria/Checkbox.mdx | 36 ++-- .../pages/react-aria/CheckboxGroup.mdx | 44 +++- .../s2-docs/pages/react-aria/RadioGroup.mdx | 61 ++++-- .../dev/s2-docs/pages/react-aria/Switch.mdx | 38 ++-- packages/dev/s2-docs/pages/s2/Checkbox.mdx | 7 +- .../dev/s2-docs/pages/s2/CheckboxGroup.mdx | 20 +- packages/dev/s2-docs/pages/s2/RadioGroup.mdx | 22 +- packages/dev/s2-docs/pages/s2/Switch.mdx | 11 +- packages/dev/s2-docs/pages/s2/home/Press.tsx | 2 +- .../react-aria-components/exports/Checkbox.ts | 11 +- .../exports/RadioGroup.ts | 4 +- .../react-aria-components/exports/Switch.ts | 11 +- .../react-aria-components/exports/index.ts | 12 +- .../react-aria-components/src/Checkbox.tsx | 203 +++++++++++++++--- .../react-aria-components/src/GridList.tsx | 8 +- .../react-aria-components/src/RadioGroup.tsx | 149 ++++++++++++- packages/react-aria-components/src/Switch.tsx | 199 ++++++++++++++++- packages/react-aria-components/src/Table.tsx | 13 +- packages/react-aria-components/src/Tree.tsx | 7 +- .../test/Checkbox.test.js | 128 ++++++++--- .../test/CheckboxGroup.test.js | 86 +++++--- .../test/GridList.test.js | 22 +- .../test/RadioGroup.test.js | 124 +++++++---- .../react-aria-components/test/Switch.test.js | 134 ++++++++++-- .../react-aria-components/test/Table.test.js | 21 +- .../react-aria-components/test/Tree.test.tsx | 14 +- .../react-aria/src/checkbox/useCheckbox.ts | 49 ++--- .../src/checkbox/useCheckboxGroupItem.ts | 2 +- packages/react-aria/src/radio/useRadio.ts | 9 +- packages/react-aria/src/switch/useSwitch.ts | 45 ++-- packages/react-aria/src/toggle/useToggle.ts | 95 ++++++-- packages/react-aria/src/utils/useSlot.ts | 53 +++++ starters/docs/src/Checkbox.css | 17 +- starters/docs/src/Checkbox.tsx | 30 +-- starters/docs/src/CheckboxGroup.css | 3 +- starters/docs/src/GridList.css | 47 ++-- starters/docs/src/RadioGroup.css | 19 +- starters/docs/src/RadioGroup.tsx | 22 +- starters/docs/src/Switch.css | 22 +- starters/docs/src/Switch.tsx | 32 +-- starters/docs/src/Table.css | 2 +- starters/docs/src/Tree.css | 2 +- starters/tailwind/src/Checkbox.tsx | 43 ++-- starters/tailwind/src/Field.tsx | 4 +- starters/tailwind/src/RadioGroup.tsx | 26 ++- starters/tailwind/src/Switch.tsx | 33 +-- 61 files changed, 1835 insertions(+), 583 deletions(-) create mode 100644 packages/react-aria/src/utils/useSlot.ts diff --git a/packages/@adobe/react-spectrum/src/checkbox/Checkbox.tsx b/packages/@adobe/react-spectrum/src/checkbox/Checkbox.tsx index 65d0d4e82fc..34c2a3bab66 100644 --- a/packages/@adobe/react-spectrum/src/checkbox/Checkbox.tsx +++ b/packages/@adobe/react-spectrum/src/checkbox/Checkbox.tsx @@ -68,7 +68,7 @@ export const Checkbox = forwardRef(function Checkbox(props: SpectrumCheckboxProp // This is a bit unorthodox. Typically, hooks cannot be called in a conditional, // but since the checkbox won't move in and out of a group, it should be safe. let groupState = useContext(CheckboxGroupContext); - let {inputProps, isInvalid, isDisabled} = groupState + let {labelProps, inputProps, isInvalid, isDisabled} = groupState // eslint-disable-next-line react-hooks/rules-of-hooks ? useCheckboxGroupItem({ ...props, @@ -104,6 +104,7 @@ export const Checkbox = forwardRef(function Checkbox(props: SpectrumCheckboxProp return (