Skip to content

Commit 5f30d47

Browse files
LFDanLudevongovettsnowystinger
authored
chore: Release audit (#7850)
* Fix autocomplete enterKeyHint in React 16 Also updates to "go" instead of "enter" * Update homepage to say "Over 50 components" 🥳 * mark filter and useFilteredListState as UNSTABLE * update Tree feature list to include virtualization * add toast to monopackage * export Collection from monopackage and use it in TreeView docs * remove element specific checks for the editableSegments query selector * remove extra dep on tree * refactor useDatePickerGroup to consider all focusable elements and avoid mutation observer --------- Co-authored-by: Devon Govett <devongovett@gmail.com> Co-authored-by: Robert Snow <rsnow@adobe.com>
1 parent acce72a commit 5f30d47

File tree

21 files changed

+55
-82
lines changed

21 files changed

+55
-82
lines changed

packages/@adobe/react-spectrum/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
},
3939
"dependencies": {
4040
"@internationalized/string": "^3.2.5",
41+
"@react-aria/collections": "3.0.0-alpha.7",
4142
"@react-aria/i18n": "^3.12.5",
4243
"@react-aria/ssr": "^3.9.7",
4344
"@react-aria/utils": "^3.27.0",
@@ -91,6 +92,7 @@
9192
"@react-spectrum/theme-dark": "^3.5.15",
9293
"@react-spectrum/theme-default": "^3.5.15",
9394
"@react-spectrum/theme-light": "^3.4.15",
95+
"@react-spectrum/toast": "3.0.0-beta.18",
9496
"@react-spectrum/tooltip": "^3.7.1",
9597
"@react-spectrum/tree": "^3.0.0-beta.3",
9698
"@react-spectrum/view": "^3.6.15",

packages/@adobe/react-spectrum/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export {TextArea, TextField} from '@react-spectrum/textfield';
6161
export {theme as darkTheme} from '@react-spectrum/theme-dark';
6262
export {theme as defaultTheme} from '@react-spectrum/theme-default';
6363
export {theme as lightTheme} from '@react-spectrum/theme-light';
64+
export {ToastContainer, ToastQueue} from '@react-spectrum/toast';
6465
export {Tooltip, TooltipTrigger} from '@react-spectrum/tooltip';
6566
export {TreeView, TreeViewItem, TreeViewItemContent} from '@react-spectrum/tree';
6667
export {Content, Footer, Header, View} from '@react-spectrum/view';
@@ -71,6 +72,7 @@ export {VisuallyHidden} from '@react-aria/visually-hidden';
7172
export {useCollator, useDateFormatter, useFilter, useLocale, useLocalizedStringFormatter, useMessageFormatter, useNumberFormatter} from '@react-aria/i18n';
7273
export {SSRProvider} from '@react-aria/ssr';
7374
export {useDragAndDrop, DIRECTORY_DRAG_TYPE} from '@react-spectrum/dnd';
75+
export {Collection} from '@react-aria/collections';
7476

7577
export type {SpectrumActionBarContainerProps, SpectrumActionBarProps} from '@react-spectrum/actionbar';
7678
export type {SpectrumActionGroupProps} from '@react-spectrum/actiongroup';
@@ -115,6 +117,7 @@ export type {SpectrumTableProps, SpectrumColumnProps, TableHeaderProps, TableBod
115117
export type {SpectrumTabListProps, SpectrumTabPanelsProps, SpectrumTabsProps} from '@react-spectrum/tabs';
116118
export type {SpectrumTagGroupProps} from '@react-spectrum/tag';
117119
export type {SpectrumTextFieldProps, SpectrumTextAreaProps} from '@react-spectrum/textfield';
120+
export type {SpectrumToastContainerProps, SpectrumToastOptions} from '@react-spectrum/toast';
118121
export type {SpectrumTooltipProps, SpectrumTooltipTriggerProps} from '@react-spectrum/tooltip';
119122
export type {SpectrumTreeViewProps, SpectrumTreeViewItemProps, SpectrumTreeViewItemContentProps} from '@react-spectrum/tree';
120123
export type {ContentProps, FooterProps, HeaderProps, ViewProps} from '@react-spectrum/view';

packages/@react-aria/autocomplete/src/useAutocomplete.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {dispatchVirtualBlur, dispatchVirtualFocus, moveVirtualFocus} from '@reac
1818
import {getInteractionModality} from '@react-aria/interactions';
1919
// @ts-ignore
2020
import intlMessages from '../intl/*.json';
21-
import React, {FocusEvent as ReactFocusEvent, KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo, useRef} from 'react';
21+
import {FocusEvent as ReactFocusEvent, KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo, useRef} from 'react';
2222
import {useLocalizedStringFormatter} from '@react-aria/i18n';
2323

2424
export interface CollectionOptions extends DOMProps, AriaLabelingProps {
@@ -350,7 +350,7 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Autocompl
350350
autoCorrect: 'off',
351351
// This disable's the macOS Safari spell check auto corrections.
352352
spellCheck: 'false',
353-
[parseInt(React.version, 10) >= 17 ? 'enterKeyHint' : 'enterkeyhint']: 'enter',
353+
enterKeyHint: 'go',
354354
onBlur,
355355
onFocus
356356
},

packages/@react-aria/collections/src/BaseCollection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ export class BaseCollection<T> implements ICollection<Node<T>> {
217217
// Will need to handle varying levels I assume but will revisit after I get searchable menu working for base menu
218218
// TODO: an alternative is to simply walk the collection and add all item nodes that match the filter and any sections/separators we encounter
219219
// to an array, then walk that new array and fix all the next/Prev keys while adding them to the new collection
220-
filter(filterFn: (nodeValue: string) => boolean): BaseCollection<T> {
220+
UNSTABLE_filter(filterFn: (nodeValue: string) => boolean): BaseCollection<T> {
221221
let newCollection = new BaseCollection<T>();
222222
// This tracks the absolute last node we've visited in the collection when filtering, used for setting up the filteredCollection's lastKey and
223223
// for updating the next/prevKey for every non-filtered node.

packages/@react-aria/datepicker/src/useDatePickerGroup.ts

Lines changed: 28 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,14 @@
11
import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
22
import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
33
import {FocusableElement, KeyboardEvent, RefObject} from '@react-types/shared';
4-
import {mergeProps, useLayoutEffect} from '@react-aria/utils';
4+
import {mergeProps} from '@react-aria/utils';
55
import {useLocale} from '@react-aria/i18n';
6-
import {useMemo, useRef} from 'react';
6+
import {useMemo} from 'react';
77
import {usePress} from '@react-aria/interactions';
88

99
export function useDatePickerGroup(state: DatePickerState | DateRangePickerState | DateFieldState, ref: RefObject<Element | null>, disableArrowNavigation?: boolean) {
1010
let {direction} = useLocale();
1111
let focusManager = useMemo(() => createFocusManager(ref), [ref]);
12-
let segments = useRef<FocusableElement[]>(undefined);
13-
useLayoutEffect(() => {
14-
if (ref?.current) {
15-
16-
let update = () => {
17-
if (ref.current) {
18-
// TODO: For now, just querying this list of elements. However, it's possible that either through hooks or RAC that some users may include other focusable items that they would want to able to keyboard navigate to. In that case, we might want to utilize focusableElements in isFocusable.ts
19-
let editableSegments: NodeListOf<Element> | undefined = ref.current?.querySelectorAll('span[role="spinbutton"], span[role="textbox"], button, div[role="spinbutton"], div[role="textbox"]');
20-
21-
let segmentsArr = Array.from(editableSegments as NodeListOf<Element>).filter(Boolean).map(node => {
22-
return {
23-
element: node as FocusableElement,
24-
rectX: node.getBoundingClientRect().left
25-
};
26-
});
27-
28-
let orderedSegments = segmentsArr.sort((a, b) => a.rectX - b.rectX).map((item => item.element));
29-
segments.current = orderedSegments;
30-
}
31-
};
32-
33-
update();
34-
35-
let observer = new MutationObserver(update);
36-
observer.observe(ref.current, {
37-
subtree: true,
38-
childList: true
39-
});
40-
41-
return () => {
42-
observer.disconnect();
43-
};
44-
}
45-
}, []);
4612

4713
// Open the popover on alt + arrow down
4814
let onKeyDown = (e: KeyboardEvent) => {
@@ -65,19 +31,12 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
6531
e.preventDefault();
6632
e.stopPropagation();
6733
if (direction === 'rtl') {
68-
if (segments.current) {
69-
let orderedSegments = segments.current;
34+
if (ref.current) {
7035
let target = e.target as FocusableElement;
71-
let index = orderedSegments.indexOf(target);
36+
let prev = findNextSegment(ref.current, target.getBoundingClientRect().left, -1);
7237

73-
if (index === 0) {
74-
target = orderedSegments[0] || target;
75-
} else {
76-
target = orderedSegments[index - 1] || target;
77-
}
78-
79-
if (target) {
80-
target.focus();
38+
if (prev) {
39+
prev.focus();
8140
}
8241
}
8342
} else {
@@ -88,22 +47,12 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
8847
e.preventDefault();
8948
e.stopPropagation();
9049
if (direction === 'rtl') {
91-
if (segments.current) {
92-
let orderedSegments = segments.current;
50+
if (ref.current) {
9351
let target = e.target as FocusableElement;
94-
let index = orderedSegments.indexOf(target);
95-
96-
if (index === orderedSegments.length - 1) {
97-
target = orderedSegments[orderedSegments.length - 1] || target;
98-
} else {
99-
target = orderedSegments[index - 1] || target;
100-
}
52+
let next = findNextSegment(ref.current, target.getBoundingClientRect().left, 1);
10153

102-
103-
target = orderedSegments[index + 1] || target;
104-
105-
if (target) {
106-
target.focus();
54+
if (next) {
55+
next.focus();
10756
}
10857
}
10958
} else {
@@ -169,3 +118,21 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
169118

170119
return mergeProps(pressProps, {onKeyDown});
171120
}
121+
122+
function findNextSegment(group: Element, fromX: number, direction: number) {
123+
let walker = getFocusableTreeWalker(group, {tabbable: true});
124+
let node = walker.nextNode();
125+
let closest: FocusableElement | null = null;
126+
let closestDistance = Infinity;
127+
while (node) {
128+
let x = (node as Element).getBoundingClientRect().left;
129+
let distance = x - fromX;
130+
let absoluteDistance = Math.abs(distance);
131+
if (Math.sign(distance) === direction && absoluteDistance < closestDistance) {
132+
closest = node as FocusableElement;
133+
closestDistance = absoluteDistance;
134+
}
135+
node = walker.nextNode();
136+
}
137+
return closest;
138+
}

packages/@react-aria/textfield/src/useTextField.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export interface AriaTextFieldOptions<T extends TextFieldIntrinsicElements> exte
8585
/**
8686
* An enumerated attribute that defines what action label or icon to preset for the enter key on virtual keyboards. See [https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint].
8787
*/
88-
enterKeyHint?: string
88+
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
8989
}
9090

9191
/**

packages/@react-spectrum/tree/docs/TreeView.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ The `<TreeViewItem>` component works with frameworks and client side routers lik
448448
### With action menus
449449

450450
```tsx example
451-
import {Collection} from 'react-aria-components';
451+
import {Collection} from '@adobe/react-spectrum';
452452

453453
<TreeView aria-label="Example tree with action menus" height="size-3000" maxWidth="size-6000" items={items}>
454454
{function renderItem(item: MyItem) {

packages/@react-stately/list/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212

1313
export type {ListProps, ListState} from './useListState';
1414
export type {SingleSelectListProps, SingleSelectListState} from './useSingleSelectListState';
15-
export {useListState, useFilteredListState} from './useListState';
15+
export {useListState, UNSTABLE_useFilteredListState} from './useListState';
1616
export {useSingleSelectListState} from './useSingleSelectListState';
1717
export {ListCollection} from './ListCollection';

packages/@react-stately/list/src/useListState.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export function useListState<T extends object>(props: ListProps<T>): ListState<T
7373
/**
7474
* Filters a collection using the provided filter function and returns a new ListState.
7575
*/
76-
export function useFilteredListState<T extends object>(state: ListState<T>, filterFn: ((nodeValue: string) => boolean) | null | undefined): ListState<T> {
77-
let collection = useMemo(() => filterFn ? state.collection.filter!(filterFn) : state.collection, [state.collection, filterFn]);
76+
export function UNSTABLE_useFilteredListState<T extends object>(state: ListState<T>, filterFn: ((nodeValue: string) => boolean) | null | undefined): ListState<T> {
77+
let collection = useMemo(() => filterFn ? state.collection.UNSTABLE_filter!(filterFn) : state.collection, [state.collection, filterFn]);
7878
let selectionManager = state.selectionManager.withCollection(collection);
7979
useFocusedKeyReset(collection, selectionManager);
8080
return {

packages/@react-types/searchfield/src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export interface AriaSearchFieldProps extends SearchFieldProps, AriaTextFieldPro
2525
/**
2626
* An enumerated attribute that defines what action label or icon to preset for the enter key on virtual keyboards. See [https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/enterkeyhint].
2727
*/
28-
enterKeyHint?: string
28+
enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
2929
}
3030
export interface SpectrumSearchFieldProps extends SpectrumTextInputBase, Omit<AriaSearchFieldProps, 'isInvalid' | 'validationState'>, SpectrumTextFieldProps {}

0 commit comments

Comments
 (0)