diff --git a/packages/@react-spectrum/s2/test/Picker.test.tsx b/packages/@react-spectrum/s2/test/Picker.test.tsx
index 20192fd6b39..05d59184f1e 100644
--- a/packages/@react-spectrum/s2/test/Picker.test.tsx
+++ b/packages/@react-spectrum/s2/test/Picker.test.tsx
@@ -180,40 +180,6 @@ describe('Picker', () => {
expect(tree.getByTestId('custom-value')).toHaveTextContent('Chocolate, Vanilla');
});
- it('supports shift+click to select a range in multi-selection', async () => {
- let user = userEvent.setup({delay: null, pointerMap});
- let items = [
- {id: 'chocolate', name: 'Chocolate'},
- {id: 'strawberry', name: 'Strawberry'},
- {id: 'vanilla', name: 'Vanilla'}
- ];
- let tree = render(
-
- {(item: any) => (
-
- {item.name}
-
- )}
-
- );
-
- let selectTester = testUtilUser.createTester('Select', {
- root: tree.container,
- interactionType: 'mouse'
- });
- await selectTester.open();
- let options = selectTester.getOptions();
-
- await user.click(options[0]);
- await user.keyboard('{Shift>}');
- await user.click(options[2]);
- await user.keyboard('{/Shift}');
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true');
- expect(options[1]).toHaveAttribute('aria-selected', 'true');
- expect(options[2]).toHaveAttribute('aria-selected', 'true');
- });
-
it('should warn if the custom render value output has a interactive child', async () => {
using spy = jest.spyOn(console, 'warn').mockImplementation(() => {}) as jest.SpyInstance &
Disposable;
diff --git a/packages/react-aria-components/test/Select.test.js b/packages/react-aria-components/test/Select.test.js
index 56e6ac0a251..df4906037d2 100644
--- a/packages/react-aria-components/test/Select.test.js
+++ b/packages/react-aria-components/test/Select.test.js
@@ -846,50 +846,6 @@ describe('Select', () => {
expect(trigger).toHaveTextContent('2 selected items');
});
- it('supports shift+click to select a range in multi-selection', async () => {
- let {getByTestId} = render();
- let selectTester = testUtilUser.createTester('Select', {root: getByTestId('select')});
-
- await selectTester.open();
- let options = selectTester.getOptions();
-
- await user.click(options[0]);
- expect(options[0]).toHaveAttribute('aria-selected', 'true');
-
- await user.keyboard('{Shift>}');
- await user.click(options[2]);
- await user.keyboard('{/Shift}');
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true');
- expect(options[1]).toHaveAttribute('aria-selected', 'true');
- expect(options[2]).toHaveAttribute('aria-selected', 'true');
- });
-
- it('keeps a stable anchor across consecutive shift+clicks', async () => {
- let {getByTestId} = render();
- let selectTester = testUtilUser.createTester('Select', {root: getByTestId('select')});
-
- await selectTester.open();
- let options = selectTester.getOptions();
-
- await user.click(options[0]);
-
- await user.keyboard('{Shift>}');
- await user.click(options[2]);
- expect(options[0]).toHaveAttribute('aria-selected', 'true');
- expect(options[1]).toHaveAttribute('aria-selected', 'true');
- expect(options[2]).toHaveAttribute('aria-selected', 'true');
-
- // Shift+click again from the same anchor: the range shrinks rather than the
- // anchor jumping to the previous target.
- await user.click(options[1]);
- await user.keyboard('{/Shift}');
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true');
- expect(options[1]).toHaveAttribute('aria-selected', 'true');
- expect(options[2]).toHaveAttribute('aria-selected', 'false');
- });
-
it('has a value immediately after rendering', async () => {
function Example() {
const ref = useRef(null);
diff --git a/packages/react-stately/src/select/useSelectState.ts b/packages/react-stately/src/select/useSelectState.ts
index adb6264a413..beb206ddc2c 100644
--- a/packages/react-stately/src/select/useSelectState.ts
+++ b/packages/react-stately/src/select/useSelectState.ts
@@ -29,7 +29,7 @@ import {FormValidationState, useFormValidationState} from '../form/useFormValida
import {ListState, useListState} from '../list/useListState';
import {OverlayTriggerState, useOverlayTriggerState} from '../overlays/useOverlayTriggerState';
import {useControlledState} from '../utils/useControlledState';
-import {useMemo, useRef, useState} from 'react';
+import {useMemo, useState} from 'react';
export type SelectionMode = 'single' | 'multiple';
export type ValueType = M extends 'single' ? Key | null : readonly Key[];
@@ -196,27 +196,12 @@ export function useSelectState(
}
};
- // Preserve the selection's anchor (anchorKey/currentKey) across renders. The
- // multiple-selection `value` is a plain Key[], so without this the listbox
- // would rebuild an anchorless Selection on every render and range selection
- // (shift+click / shift+arrow) would collapse to just the clicked item. We keep
- // the last Selection produced internally and feed it back while its membership
- // still matches `value`.
- let lastSelection = useRef | null>(null);
-
let listState = useListState({
...props,
selectionMode,
disallowEmptySelection: selectionMode === 'single',
allowDuplicateSelectionEvents: true,
- selectedKeys: useMemo(() => {
- let selectedKeys = convertValue(displayValue);
- let last = lastSelection.current;
- if (last != null && Array.isArray(selectedKeys) && isSameSelection(last, selectedKeys)) {
- return last;
- }
- return selectedKeys;
- }, [displayValue]),
+ selectedKeys: useMemo(() => convertValue(displayValue), [displayValue]),
onSelectionChange: (keys: Selection) => {
// impossible, but TS doesn't know that
if (keys === 'all') {
@@ -227,9 +212,6 @@ export function useSelectState(
let key = keys.values().next().value ?? null;
setValue(key);
} else {
- // Remember the Selection (with its anchor) so it survives the round-trip
- // through the plain `value` array on the next render.
- lastSelection.current = keys;
setValue([...keys]);
}
if (shouldCloseOnSelect) {
@@ -296,15 +278,3 @@ function convertValue(value: Key | Key[] | null | undefined) {
}
return Array.isArray(value) ? value : [value];
}
-
-function isSameSelection(selection: Set, keys: Key[]): boolean {
- if (selection.size !== keys.length) {
- return false;
- }
- for (let key of keys) {
- if (!selection.has(key)) {
- return false;
- }
- }
- return true;
-}