Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,30 @@ orbs:
executors:
rsp:
docker:
- image: cimg/node:22.21.1
- image: cimg/node:24.13.0
environment:
CACHE_VERSION: v1
working_directory: ~/react-spectrum

rsp-large:
docker:
- image: cimg/node:22.21.1
- image: cimg/node:24.13.0
resource_class: large
environment:
CACHE_VERSION: v1
working_directory: ~/react-spectrum

rsp-xlarge:
docker:
- image: cimg/node:22.21.1
- image: cimg/node:24.13.0
resource_class: xlarge
environment:
CACHE_VERSION: v1
working_directory: ~/react-spectrum

rsp-2xlarge:
docker:
- image: cimg/node:22.21.1
- image: cimg/node:24.13.0
resource_class: 2xlarge
environment:
CACHE_VERSION: v1
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/branch/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'Branch from fork'
description: 'creates a branch based off PR from fork'
runs:
using: 'node22'
using: 'node24'
main: 'index.js'
2 changes: 1 addition & 1 deletion .github/actions/comment/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'PR Comment'
description: 'Comment on the PR attached to a commit'
runs:
using: 'node22'
using: 'node24'
main: 'index.js'
2 changes: 1 addition & 1 deletion .github/actions/permissions/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'Check permissions'
description: 'Checks if commentor has write access or above'
runs:
using: 'node22'
using: 'node24'
main: 'index.js'
13 changes: 13 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
needs translations:
- changed-files:
- any-glob-to-any-file: ['**/intl/*.json']

S2:
- changed-files:
- any-glob-to-any-file: ['**/s2/**']

RAC:
- changed-files:
- any-glob-to-any-file: ['**/react-aria-components/**', '**/@react-aria/**']

V3:
- changed-files:
- any-glob-to-any-file: ['**/@react-spectrum/**']

2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
24
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/) (v14.15.0+) and [yarn](https://yarnpkg.com/en/) (v1.22.0+)
Make sure you have the following requirements installed: [node](https://nodejs.org/) (v24.13.0+) 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.
```
Expand Down Expand Up @@ -176,7 +176,7 @@ parcel build packages/@react-{spectrum,aria,stately}/*/ packages/@internationali
make: *** [build] Segmentation fault: 11
```

It's likely that you are using a different version of Node.js. Please use Node.js 18. When changing the node version, delete `node_modules` and re-run `yarn install`
It's likely that you are using a different version of Node.js. Please use Node.js 24. When changing the node version, delete `node_modules` and re-run `yarn install`

> `yarn start` fails.
Expand Down
6 changes: 6 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ export default [{
"rsp-rules/no-react-key": [ERROR],
"rsp-rules/sort-imports": [ERROR],
"rsp-rules/no-non-shadow-contains": [ERROR],
"rsp-rules/safe-event-target": [ERROR],
"rsp-rules/shadow-safe-active-element": [ERROR],
"rsp-rules/faster-node-contains": [ERROR],
"rulesdir/imports": [ERROR],
"rulesdir/useLayoutEffectRule": [ERROR],
Expand Down Expand Up @@ -431,6 +433,8 @@ export default [{
"rsp-rules/act-events-test": ERROR,
"rsp-rules/no-getByRole-toThrow": ERROR,
"rsp-rules/no-non-shadow-contains": OFF,
"rsp-rules/safe-event-target": OFF,
"rsp-rules/shadow-safe-active-element": OFF,
"rsp-rules/faster-node-contains": OFF,
"rulesdir/imports": OFF,
"monorepo/no-internal-import": OFF,
Expand Down Expand Up @@ -472,6 +476,7 @@ export default [{
rules: {
"jsdoc/require-jsdoc": OFF,
"jsdoc/require-description": OFF,
"rsp-rules/safe-event-target": OFF,
},
}, {
files: [
Expand Down Expand Up @@ -512,6 +517,7 @@ export default [{
rules: {
"rsp-rules/faster-node-contains": OFF,
"rsp-rules/no-non-shadow-contains": OFF,
"rsp-rules/shadow-safe-active-element": OFF,
},
}, {
files: ["packages/@react-spectrum/s2/**", "packages/dev/s2-docs/**"],
Expand Down
2 changes: 1 addition & 1 deletion examples/remix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"vite-tsconfig-paths": "^4.2.1"
},
"engines": {
"node": ">=22.0.0"
"node": ">=24.0.0"
},
"workspaces": [
"../../packages/react-aria-components",
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
"lerna": "^3.13.2",
"lucide-react": "^0.517.0",
"md5": "^2.2.1",
"mdast-util-to-string": "^4.0.0",
"motion": "^12.23.6",
"npm-cli-login": "^1.0.0",
"parcel": "^2.16.3",
Expand Down Expand Up @@ -231,9 +232,9 @@
"ast-types": "0.16.1",
"svgo": "^3",
"@testing-library/user-event": "patch:@testing-library/user-event@npm%3A14.6.1#~/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch",
"@types/node@npm:*": "^22",
"@types/node@npm:^18.0.0": "^22",
"@types/node@npm:>= 8": "^22",
"@types/node@npm:*": "^24",
"@types/node@npm:^18.0.0": "^24",
"@types/node@npm:>= 8": "^24",
"@storybook/addon-docs@npm:8.6.14": "patch:@storybook/addon-docs@npm%3A8.6.14#~/.yarn/patches/@storybook-addon-docs-npm-8.6.14-12ab3f55f8.patch",
"@storybook/react": "patch:@storybook/react@npm%3A8.6.14#~/.yarn/patches/@storybook-react-npm-8.6.14-bc3fc2208a.patch",
"micromark-extension-mdxjs": "patch:micromark-extension-mdxjs@npm%3A1.0.0#~/.yarn/patches/micromark-extension-mdxjs-npm-1.0.0-d2b6b69e4a.patch",
Expand Down
4 changes: 3 additions & 1 deletion packages/@internationalized/date/docs/ZonedDateTime.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ let date = parseAbsoluteToLocal('2021-11-07T07:45:00Z');
You can also create a `ZonedDateTime` using a `Date` object or epoch time (milliseconds) using one of the following functions:

* <TypeLink links={docs.links} type={docs.exports.fromDate} /> – This function creates a `ZonedDateTime` from a `Date` object. A time zone identifier, e.g. `America/Los_Angeles`, must be passed, and the result will be converted into that time zone.
* <TypeLink links={docs.links} type={docs.exports.fromDateToLocal} /> – This function creates a `ZonedDateTime` from a `Date` object. The time zone identifier is automatically resolved from the current user’s environment, and the result is converted into that local time zone.
* <TypeLink links={docs.links} type={docs.exports.fromAbsolute} /> – This function creates a `ZonedDateTime` from a Unix epoch (e.g. `1688023843144`, representing milliseconds since 1970). A time zone identifier, e.g. `America/Los_Angeles`, must be provided, and the result will be converted into that time zone.

```tsx
import {fromDate, fromAbsolute} from '@internationalized/date';
import {fromDate, fromDateToLocal, fromAbsolute} from '@internationalized/date';

let date = fromDate(new Date(), 'America/Los_Angeles');
let date = fromDateToLocal(new Date());
let date = fromAbsolute(1688023843144, 'America/Los_Angeles');
```

Expand Down
3 changes: 3 additions & 0 deletions packages/@internationalized/date/src/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ export function fromDate(date: Date, timeZone: string): ZonedDateTime {
return fromAbsolute(date.getTime(), timeZone);
}

/**
* Takes a `Date` object and converts it to the time zone identifier for the current user.
*/
export function fromDateToLocal(date: Date): ZonedDateTime {
return fromDate(date, getLocalTimeZone());
}
Expand Down
1 change: 1 addition & 0 deletions packages/@internationalized/date/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export {
toTimeZone,
toLocalTimeZone,
fromDate,
fromDateToLocal,
fromAbsolute
} from './conversion';
export {
Expand Down
8 changes: 4 additions & 4 deletions packages/@react-aria/actiongroup/src/useActionGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
import {AriaActionGroupProps} from '@react-types/actiongroup';
import {createFocusManager} from '@react-aria/focus';
import {DOMAttributes, FocusableElement, Orientation, RefObject} from '@react-types/shared';
import {filterDOMProps, nodeContains, useLayoutEffect} from '@react-aria/utils';
import {filterDOMProps, getEventTarget, nodeContains, useLayoutEffect} from '@react-aria/utils';
import {KeyboardEventHandler, useState} from 'react';
import {ListState} from '@react-stately/list';
import {useLocale} from '@react-aria/i18n';
import {useState} from 'react';

const BUTTON_GROUP_ROLES = {
'none': 'toolbar',
Expand Down Expand Up @@ -47,8 +47,8 @@ export function useActionGroup<T>(props: AriaActionGroupProps<T>, state: ListSta
let {direction} = useLocale();
let focusManager = createFocusManager(ref);
let flipDirection = direction === 'rtl' && orientation === 'horizontal';
let onKeyDown = (e) => {
if (!nodeContains(e.currentTarget, e.target)) {
let onKeyDown: KeyboardEventHandler = (e) => {
if (!nodeContains(e.currentTarget, getEventTarget(e))) {
return;
}

Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/autocomplete/src/useAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaLabelingProps, BaseEvent, DOMProps, FocusableElement, FocusEvents, KeyboardEvents, Node, RefObject, ValueBase} from '@react-types/shared';
import {AriaTextFieldProps} from '@react-aria/textfield';
import {AutocompleteProps, AutocompleteState} from '@react-stately/autocomplete';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useId, useLabels, useLayoutEffect, useObjectRef} from '@react-aria/utils';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getEventTarget, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useId, useLabels, useLayoutEffect, useObjectRef} from '@react-aria/utils';
import {dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement, moveVirtualFocus} from '@react-aria/focus';
import {getInteractionModality, getPointerType} from '@react-aria/interactions';
// @ts-ignore
Expand Down Expand Up @@ -112,7 +112,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
inputRef.current.focus();
}

let target = e.target as Element | null;
let target = getEventTarget(e) as Element | null;
if (e.isTrusted || !target || queuedActiveDescendant.current === target.id) {
return;
}
Expand Down Expand Up @@ -225,7 +225,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
let keyDownTarget = useRef<Element | null>(null);
// For textfield specific keydown operations
let onKeyDown = (e: BaseEvent<ReactKeyboardEvent<any>>) => {
keyDownTarget.current = e.target as Element;
keyDownTarget.current = getEventTarget(e) as Element;
if (e.nativeEvent.isComposing) {
return;
}
Expand Down Expand Up @@ -329,7 +329,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
// Dispatch simulated key up events for things like triggering links in listbox
// Make sure to stop the propagation of the input keyup event so that the simulated keyup/down pair
// is detected by usePress instead of the original keyup originating from the input
if (e.target === keyDownTarget.current) {
if (getEventTarget(e) === keyDownTarget.current) {
e.stopImmediatePropagation();
let focusedNodeId = queuedActiveDescendant.current;
if (focusedNodeId == null) {
Expand Down Expand Up @@ -386,7 +386,7 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut

let curFocusedNode = queuedActiveDescendant.current ? document.getElementById(queuedActiveDescendant.current) : null;
if (curFocusedNode) {
let target = e.target;
let target = getEventTarget(e);
queueMicrotask(() => {
// instead of focusing the last focused node, just focus the collection instead and have the collection handle what item to focus via useSelectableCollection/Item
dispatchVirtualBlur(target, collectionRef.current);
Expand Down
17 changes: 9 additions & 8 deletions packages/@react-aria/calendar/src/useCalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {CalendarDate, isEqualDay, isSameDay, isToday} from '@internationalized/date';
import {CalendarState, RangeCalendarState} from '@react-stately/calendar';
import {DOMAttributes, RefObject} from '@react-types/shared';
import {focusWithoutScrolling, getScrollParent, mergeProps, scrollIntoViewport, useDeepMemo, useDescription} from '@react-aria/utils';
import {focusWithoutScrolling, getActiveElement, getEventTarget, getScrollParent, mergeProps, scrollIntoViewport, useDeepMemo, useDescription} from '@react-aria/utils';
import {getEraFormat, hookData} from './utils';
import {getInteractionModality, usePress} from '@react-aria/interactions';
// @ts-ignore
Expand Down Expand Up @@ -300,7 +300,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
// Also only scroll into view if the cell actually got focused.
// There are some cases where the cell might be disabled or inside,
// an inert container and we don't want to scroll then.
if (getInteractionModality() !== 'pointer' && document.activeElement === ref.current) {
if (getInteractionModality() !== 'pointer' && getActiveElement() === ref.current) {
scrollIntoViewport(ref.current, {containingElement: getScrollParent(ref.current)});
}
}
Expand Down Expand Up @@ -343,17 +343,18 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
state.highlightDate(date);
}
},
onPointerDown(e) {
onPointerDown(e: PointerEvent) {
// This is necessary on touch devices to allow dragging
// outside the original pressed element.
// (JSDOM does not support this)
if ('releasePointerCapture' in e.target) {
if ('hasPointerCapture' in e.target) {
if (e.target.hasPointerCapture(e.pointerId)) {
e.target.releasePointerCapture(e.pointerId);
let target = getEventTarget(e);
if (target instanceof HTMLElement && 'releasePointerCapture' in target) {
if ('hasPointerCapture' in target) {
if (target.hasPointerCapture(e.pointerId)) {
target.releasePointerCapture(e.pointerId);
}
} else {
e.target.releasePointerCapture(e.pointerId);
(target as HTMLElement).releasePointerCapture(e.pointerId);
}
}
},
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/color/src/useColorWheel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaColorWheelProps} from '@react-types/color';
import {ColorWheelState} from '@react-stately/color';
import {DOMAttributes, RefObject} from '@react-types/shared';
import {focusWithoutScrolling, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
import {focusWithoutScrolling, getEventTarget, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
import React, {ChangeEvent, InputHTMLAttributes, useCallback, useRef} from 'react';
import {useKeyboard, useMove} from '@react-aria/interactions';
import {useLocale} from '@react-aria/i18n';
Expand Down Expand Up @@ -328,7 +328,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
name,
form,
onChange: (e: ChangeEvent<HTMLInputElement>) => {
state.setHue(parseFloat(e.target.value));
state.setHue(parseFloat(getEventTarget(e).value));
},
style: visuallyHiddenProps.style,
'aria-errormessage': props['aria-errormessage'],
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-aria/combobox/src/useComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {AriaComboBoxProps} from '@react-types/combobox';
import {ariaHideOutside} from '@react-aria/overlays';
import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
import {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';
import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
import {chain, getActiveElement, getEventTarget, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
import {ComboBoxState} from '@react-stately/combobox';
import {dispatchVirtualFocus} from '@react-aria/focus';
import {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';
Expand Down Expand Up @@ -264,7 +264,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
return;
}

let rect = (e.target as Element).getBoundingClientRect();
let rect = (getEventTarget(e) as Element).getBoundingClientRect();
let touch = e.changedTouches[0];

let centerX = Math.ceil(rect.left + .5 * rect.width);
Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/datepicker/src/useDatePickerGroup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
import {DOMAttributes, FocusableElement, KeyboardEvent, RefObject} from '@react-types/shared';
import {mergeProps, nodeContains} from '@react-aria/utils';
import {getEventTarget, mergeProps, nodeContains} from '@react-aria/utils';
import {useLocale} from '@react-aria/i18n';
import {useMemo} from 'react';
import {usePress} from '@react-aria/interactions';
Expand All @@ -12,7 +12,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState

// Open the popover on alt + arrow down
let onKeyDown = (e: KeyboardEvent) => {
if (!nodeContains(e.currentTarget, e.target as Element)) {
if (!nodeContains(e.currentTarget, getEventTarget(e) as Element)) {
return;
}

Expand All @@ -32,7 +32,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
e.stopPropagation();
if (direction === 'rtl') {
if (ref.current) {
let target = e.target as FocusableElement;
let target = getEventTarget(e) as FocusableElement;
let prev = findNextSegment(ref.current, target.getBoundingClientRect().left, -1);

if (prev) {
Expand All @@ -48,7 +48,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
e.stopPropagation();
if (direction === 'rtl') {
if (ref.current) {
let target = e.target as FocusableElement;
let target = getEventTarget(e) as FocusableElement;
let next = findNextSegment(ref.current, target.getBoundingClientRect().left, 1);

if (next) {
Expand All @@ -68,7 +68,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
return;
}
// Try to find the segment prior to the element that was clicked on.
let target = window.event?.target as FocusableElement;
let target = window.event ? getEventTarget(window.event) as FocusableElement : null;
let walker = getFocusableTreeWalker(ref.current, {tabbable: true});
if (target) {
walker.currentNode = target;
Expand Down
Loading
Loading