Skip to content

Commit 68af923

Browse files
authored
Revert change to add 'all' to expandedKeys and other tree followups (#6002)
* some updates from reviews * reseting expandedKeys "all" change * default level value to fix lint
1 parent e6923d9 commit 68af923

File tree

10 files changed

+42
-57
lines changed

10 files changed

+42
-57
lines changed

packages/@react-aria/accordion/src/useAccordion.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function useAccordionItem<T>(props: AccordionItemAriaProps<T>, state: Tre
5050
isDisabled,
5151
onPress: () => state.toggleKey(item.key)
5252
}), ref);
53-
let isExpanded = state.expandedKeys === 'all' || state.expandedKeys.has(item.key);
53+
let isExpanded = state.expandedKeys.has(item.key);
5454
return {
5555
buttonProps: {
5656
...buttonProps,

packages/@react-aria/gridlist/src/useGridListItem.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
9494
onAction = () => state.toggleKey(node.key);
9595
}
9696

97-
let isExpanded = hasChildRows ? state.expandedKeys === 'all' || state.expandedKeys.has(node.key) : undefined;
97+
let isExpanded = hasChildRows ? state.expandedKeys.has(node.key) : undefined;
9898
treeGridRowProps = {
9999
'aria-expanded': isExpanded,
100100
'aria-level': node.level + 1,
@@ -125,11 +125,11 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
125125
walker.currentNode = document.activeElement;
126126

127127
if ('expandedKeys' in state && document.activeElement === ref.current) {
128-
if ((e.key === EXPANSION_KEYS['expand'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && state.expandedKeys !== 'all' && !state.expandedKeys.has(node.key)) {
128+
if ((e.key === EXPANSION_KEYS['expand'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && !state.expandedKeys.has(node.key)) {
129129
state.toggleKey(node.key);
130130
e.stopPropagation();
131131
return;
132-
} else if ((e.key === EXPANSION_KEYS['collapse'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && (state.expandedKeys === 'all' || state.expandedKeys.has(node.key))) {
132+
} else if ((e.key === EXPANSION_KEYS['collapse'][direction]) && state.selectionManager.focusedKey === node.key && hasChildRows && state.expandedKeys.has(node.key)) {
133133
state.toggleKey(node.key);
134134
e.stopPropagation();
135135
return;

packages/@react-spectrum/accordion/src/Accordion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function AccordionItem<T>(props: AccordionItemProps<T>) {
5757
let ref = useRef<HTMLButtonElement>(null);
5858
let {state, item} = props;
5959
let {buttonProps, regionProps} = useAccordionItem<T>(props, state, ref);
60-
let isOpen = state.expandedKeys === 'all' || state.expandedKeys.has(item.key);
60+
let isOpen = state.expandedKeys.has(item.key);
6161
let isDisabled = state.disabledKeys.has(item.key);
6262
let {isHovered, hoverProps} = useHover({isDisabled});
6363
let {direction} = useLocale();

packages/@react-spectrum/tree/src/Tree.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ function TreeItem<T>(props: TreeItemProps<T>) {
7777
hasChildNodes
7878
} = item;
7979

80-
let isExpanded = state.expandedKeys === 'all' || state.expandedKeys.has(key);
80+
let isExpanded = state.expandedKeys.has(key);
8181
let isSelected = state.selectionManager.isSelected(key);
8282

8383
let itemClassName = classNames(styles, 'spectrum-TreeView-item', {

packages/@react-stately/tree/src/TreeCollection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ export class TreeCollection<T> implements Collection<Node<T>> {
1818
private firstKey: Key;
1919
private lastKey: Key;
2020

21-
constructor(nodes: Iterable<Node<T>>, {expandedKeys}: {expandedKeys?: 'all' | Set<Key>} = {}) {
21+
constructor(nodes: Iterable<Node<T>>, {expandedKeys}: {expandedKeys?: Set<Key>} = {}) {
2222
this.iterable = nodes;
2323
expandedKeys = expandedKeys || new Set();
2424

2525
let visit = (node: Node<T>) => {
2626
this.keyMap.set(node.key, node);
2727

28-
if (node.childNodes && (node.type === 'section' || (expandedKeys === 'all' || expandedKeys.has(node.key)))) {
28+
if (node.childNodes && (node.type === 'section' || expandedKeys.has(node.key))) {
2929
for (let child of node.childNodes) {
3030
visit(child);
3131
}

packages/@react-stately/tree/src/useTreeState.ts

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ export interface TreeState<T> {
2929
readonly disabledKeys: Set<Key>,
3030

3131
/** A set of keys for items that are expanded. */
32-
readonly expandedKeys: 'all' | Set<Key>,
32+
readonly expandedKeys: Set<Key>,
3333

3434
/** Toggles the expanded state for an item by its key. */
3535
toggleKey(key: Key): void,
3636

3737
/** Replaces the set of expanded keys. */
38-
setExpandedKeys(keys: 'all' | Set<Key>): void,
38+
setExpandedKeys(keys: Set<Key>): void,
3939

4040
/** A selection manager to read and update multiple selection state. */
4141
readonly selectionManager: SelectionManager
@@ -47,14 +47,12 @@ export interface TreeState<T> {
4747
*/
4848
export function useTreeState<T extends object>(props: TreeProps<T>): TreeState<T> {
4949
let {
50-
expandedKeys: propExpandedKeys,
51-
defaultExpandedKeys: propDefaultExpandedKeys,
5250
onExpandedChange
5351
} = props;
5452

5553
let [expandedKeys, setExpandedKeys] = useControlledState(
56-
propExpandedKeys ? convertExpanded(propExpandedKeys) : undefined,
57-
propDefaultExpandedKeys ? convertExpanded(propDefaultExpandedKeys) : new Set(),
54+
props.expandedKeys ? new Set(props.expandedKeys) : undefined,
55+
props.defaultExpandedKeys ? new Set(props.defaultExpandedKeys) : new Set(),
5856
onExpandedChange
5957
);
6058

@@ -74,7 +72,7 @@ export function useTreeState<T extends object>(props: TreeProps<T>): TreeState<T
7472
}, [tree, selectionState.focusedKey]);
7573

7674
let onToggle = (key: Key) => {
77-
setExpandedKeys(toggleKey(expandedKeys, key, tree));
75+
setExpandedKeys(toggleKey(expandedKeys, key));
7876
};
7977

8078
return {
@@ -87,32 +85,13 @@ export function useTreeState<T extends object>(props: TreeProps<T>): TreeState<T
8785
};
8886
}
8987

90-
function toggleKey<T>(currentExpandedKeys: 'all' | Set<Key>, key: Key, collection: Collection<Node<T>>): Set<Key> {
91-
let updatedExpandedKeys: Set<Key>;
92-
if (currentExpandedKeys === 'all') {
93-
// TODO: would be nice if the collection row information differentiated between childNodes vs childItems
94-
// so we didn't have to keep iterating through info, perhaps make the user pass a prop to TreeItem for childItems/hasChildRows even in the static case?
95-
updatedExpandedKeys = new Set([...collection].filter(row => {
96-
return row.props.childItems || [...collection.getChildren(row.key)].filter(child => child.type === 'item').length > 0;
97-
}).map(row => row.key));
98-
updatedExpandedKeys.delete(key);
88+
function toggleKey(set: Set<Key>, key: Key): Set<Key> {
89+
let res = new Set(set);
90+
if (res.has(key)) {
91+
res.delete(key);
9992
} else {
100-
updatedExpandedKeys = new Set(currentExpandedKeys);
101-
if (updatedExpandedKeys.has(key)) {
102-
updatedExpandedKeys.delete(key);
103-
} else {
104-
updatedExpandedKeys.add(key);
105-
}
106-
}
107-
return updatedExpandedKeys;
108-
}
109-
110-
function convertExpanded(expanded: 'all' | Iterable<Key>): 'all' | Set<Key> {
111-
if (!expanded) {
112-
return new Set<Key>();
93+
res.add(key);
11394
}
11495

115-
return expanded === 'all'
116-
? 'all'
117-
: new Set(expanded);
96+
return res;
11897
}

packages/@react-types/shared/src/collections.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ export interface CollectionStateBase<T, C extends Collection<Node<T>> = Collecti
7171

7272
export interface Expandable {
7373
/** The currently expanded keys in the collection (controlled). */
74-
expandedKeys?: 'all' | Iterable<Key>,
74+
expandedKeys?: Iterable<Key>,
7575
/** The initial expanded keys in the collection (uncontrolled). */
76-
defaultExpandedKeys?: 'all' | Iterable<Key>,
76+
defaultExpandedKeys?: Iterable<Key>,
7777
/** Handler that is called when items are expanded or collapsed. */
7878
onExpandedChange?: (keys: Set<Key>) => any
7979
}

packages/react-aria-components/src/Tree.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {FocusScope, mergeProps, useFocusRing, useGridListSelectionCheckbox, use
2121
import {Collection as ICollection, Node, SelectionBehavior, TreeState, useTreeState} from 'react-stately';
2222
// @ts-ignore
2323
import intlMessages from '../intl/*.json';
24-
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactElement, ReactNode, RefObject, useContext, useEffect, useMemo, useRef} from 'react';
24+
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactNode, RefObject, useContext, useEffect, useMemo, useRef} from 'react';
2525
import {useControlledState} from '@react-stately/utils';
2626

2727
class TreeCollection<T> implements ICollection<Node<T>> {
@@ -283,8 +283,8 @@ export interface TreeItemProps<T = object> extends StyleRenderProps<TreeItemRend
283283
/** A list of child tree row objects used when dynamically rendering the tree row children. */
284284
childItems?: Iterable<T>,
285285
// TODO: made this required since the user needs to pass Content at least
286-
/** The content of the tree row along with any nested children. Supports static items or a function for dynamic rendering. */
287-
children: ReactNode | ((item: T) => ReactElement)
286+
/** The content of the tree row along with any nested children. Supports static nested tree rows or use of a Collection to dynamically render nested tree rows. */
287+
children: ReactNode
288288
}
289289

290290
function TreeItem<T extends object>(props: TreeItemProps<T>, ref: ForwardedRef<HTMLDivElement>): JSX.Element | null {
@@ -339,14 +339,16 @@ export function TreeItemContent(props: TreeItemContentProps) {
339339
}
340340
}
341341

342+
export const TreeItemContentContext = createContext<TreeItemContentRenderProps | null>(null);
343+
342344
function TreeRow<T>({item}: {item: Node<T>}) {
343345
let state = useContext(TreeStateContext)!;
344346
let ref = useObjectRef<HTMLDivElement>(item.props.ref);
345347
let {rowProps, gridCellProps, ...states} = useTreeGridListItem({node: item}, state, ref);
346348
let stringFormatter = useLocalizedStringFormatter(intlMessages, 'react-aria-components');
347349
let isExpanded = rowProps['aria-expanded'] === true;
348350
let hasChildRows = [...state.collection.getChildren!(item.key)]?.length > 1;
349-
let level = rowProps['aria-level'];
351+
let level = rowProps['aria-level'] || 1;
350352

351353
let {hoverProps, isHovered} = useHover({
352354
isDisabled: !states.allowsSelection && !states.hasAction
@@ -359,7 +361,7 @@ function TreeRow<T>({item}: {item: Node<T>}) {
359361
);
360362

361363
let props: TreeItemProps<unknown> = item.props;
362-
let renderPropValues = React.useMemo(() => ({
364+
let renderPropValues = React.useMemo<TreeItemContentRenderProps>(() => ({
363365
...states,
364366
isHovered,
365367
isFocusVisible,
@@ -410,7 +412,7 @@ function TreeRow<T>({item}: {item: Node<T>}) {
410412
children: item => {
411413
switch (item.type) {
412414
case 'content': {
413-
return <TreeRowContent values={renderPropValues} item={item} />;
415+
return <TreeRowContent item={item} />;
414416
}
415417
// Skip item since we don't render the nested rows as children of the parent row, the flattened collection
416418
// will render them each as siblings instead
@@ -419,9 +421,7 @@ function TreeRow<T>({item}: {item: Node<T>}) {
419421
default:
420422
throw new Error('Unsupported element type in TreeRow: ' + item.type);
421423
}
422-
},
423-
// TODO: double check if this is the best way to go about making sure TreeRowContent's render props is always up to date
424-
dependencies: [renderPropValues]
424+
}
425425
});
426426

427427
return (
@@ -460,6 +460,9 @@ function TreeRow<T>({item}: {item: Node<T>}) {
460460
ref: expandButtonRef
461461
}
462462
}
463+
}],
464+
[TreeItemContentContext, {
465+
...renderPropValues
463466
}]
464467
]}>
465468
{children}
@@ -471,7 +474,8 @@ function TreeRow<T>({item}: {item: Node<T>}) {
471474
}
472475

473476
// This is separate from TreeItemContent since it needs to call useRenderProps
474-
function TreeRowContent({item, values}) {
477+
function TreeRowContent({item}) {
478+
let values = useContext(TreeItemContentContext);
475479
let renderProps = useRenderProps({
476480
children: item.rendered,
477481
values

packages/react-aria-components/stories/Tree.stories.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,10 @@ const DynamicTreeItem = (props: DynamicTreeItemProps) => {
247247
);
248248
};
249249

250+
let defaultExpandedKeys = new Set(['projects', 'project-2', 'project-5', 'reports', 'reports-1', 'reports-1A', 'reports-1AB']);
251+
250252
export const TreeExampleDynamic = (args: TreeProps<unknown>) => (
251-
<Tree {...args} defaultExpandedKeys="all" disabledKeys={['reports-1AB']} className={styles.tree} aria-label="test dynamic tree" items={rows} onExpandedChange={action('onExpandedChange')} onSelectionChange={action('onSelectionChange')}>
253+
<Tree {...args} defaultExpandedKeys={defaultExpandedKeys} disabledKeys={['reports-1AB']} className={styles.tree} aria-label="test dynamic tree" items={rows} onExpandedChange={action('onExpandedChange')} onSelectionChange={action('onSelectionChange')}>
252254
{(item) => (
253255
<DynamicTreeItem childItems={item.childItems} textValue={item.name}>
254256
{item.name}
@@ -273,7 +275,7 @@ export const WithActions = {
273275
};
274276

275277
export const WithLinks = (args: TreeProps<unknown>) => (
276-
<Tree {...args} defaultExpandedKeys="all" className={styles.tree} aria-label="test dynamic tree" items={rows} onExpandedChange={action('onExpandedChange')} onSelectionChange={action('onSelectionChange')}>
278+
<Tree {...args} defaultExpandedKeys={defaultExpandedKeys} className={styles.tree} aria-label="test dynamic tree" items={rows} onExpandedChange={action('onExpandedChange')} onSelectionChange={action('onSelectionChange')}>
277279
{(item) => (
278280
<DynamicTreeItem href="https://adobe.com/" childItems={item.childItems} textValue={item.name}>
279281
{item.name}

packages/react-aria-components/test/Tree.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ let StaticTreeItem = (props) => {
4141
};
4242

4343
let StaticTree = ({treeProps = {}, rowProps = {}}) => (
44-
<Tree defaultExpandedKeys="all" aria-label="test tree" onExpandedChange={onExpandedChange} onSelectionChange={onSelectionChange} {...treeProps}>
44+
<Tree defaultExpandedKeys={new Set(['projects', 'projects-1'])} aria-label="test tree" onExpandedChange={onExpandedChange} onSelectionChange={onSelectionChange} {...treeProps}>
4545
<StaticTreeItem id="Photos" textValue="Photos" {...rowProps}>Photos</StaticTreeItem>
4646
<StaticTreeItem id="projects" textValue="Projects" title="Projects" {...rowProps}>
4747
<StaticTreeItem id="projects-1" textValue="Projects-1" title="Projects-1" {...rowProps}>
@@ -117,7 +117,7 @@ let DynamicTreeItem = (props) => {
117117
};
118118

119119
let DynamicTree = ({treeProps = {}, rowProps = {}}) => (
120-
<Tree defaultExpandedKeys="all" aria-label="test dynamic tree" items={rows} onExpandedChange={onExpandedChange} onSelectionChange={onSelectionChange} {...treeProps}>
120+
<Tree defaultExpandedKeys={new Set(['projects', 'project-2', 'project-5', 'reports', 'reports-1', 'reports-1A', 'reports-1AB'])} aria-label="test dynamic tree" items={rows} onExpandedChange={onExpandedChange} onSelectionChange={onSelectionChange} {...treeProps}>
121121
{(item: any) => (
122122
<DynamicTreeItem childItems={item.childItems} textValue={item.name} {...rowProps}>
123123
{item.name}
@@ -884,7 +884,7 @@ describe('Tree', () => {
884884
});
885885

886886
it('should not expand/collapse if disabledBehavior is "all" and the row is disabled', async () => {
887-
let {getAllByRole, rerender} = render(<DynamicTree treeProps={{disabledKeys: ['projects'], disabledBehavior: 'all', expandedKeys: 'all'}} />);
887+
let {getAllByRole, rerender} = render(<DynamicTree treeProps={{disabledKeys: ['projects'], disabledBehavior: 'all', expandedKeys: new Set(['projects', 'project-2', 'project-5', 'reports', 'reports-1', 'reports-1A', 'reports-1AB'])}} />);
888888
let rows = getAllByRole('row');
889889
expect(rows).toHaveLength(20);
890890

0 commit comments

Comments
 (0)