-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: S2 treeview #7343
feat: S2 treeview #7343
Conversation
# Conflicts: # packages/@react-spectrum/s2/package.json # yarn.lock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let {isExpanded, isDisabled} = fullProps; | ||
let {direction} = useLocale(); | ||
|
||
// Will need to keep the chevron as a button for iOS VO at all times since VO doesn't focus the cell. Also keep as button if cellAction is defined by the user in the future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this not possible with RAC? What do we do in the RAC docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this got copied over from v3, @LFDanLu any idea what this is about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the chevron wasn't a button, iOS VO can't focus it and thus users can't toggle expansion of the row. If the tree row didn't have a row action, focusing the tree cell and double tapping would trigger expansion and thus this wouldn't be needed, but iOS doesn't focus grid cells in general, at least for iOS 17.
In RAC, it is up to the user to provide ALL the content of the TreeRow (including the chevron button) themselves hence why https://react-spectrum.adobe.com/react-aria/Tree.html#example has the a Button with the chevron slot. Here in S2/v3 we can afford to do that for the user thus we set it up here. If we want to provide a ChevronButton wrapper of sorts to expose from RAC we could go that route and ditch defining it at the S2/v3 level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why are we using hooks here? Why is it a span instead of a button?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I remember correctly, it was for the tabIndex handling below. I forget if we had all of the button props that we had before like excludeFromTabOrder
and preventFocusOnPress
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems to work with the props @LFDanLu has suggested, but will need to verify on any Androids the team has
|
||
let layout = useMemo(() => { | ||
return new UNSTABLE_ListLayout({ | ||
rowHeight: isDetached ? 44 : 40 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing conflicting designs in Figma for the spacing between the detached rows, but the tokens file says that it should use tree-view-item-to-item-detached
which is 2px whereas the design file has 8px between each row...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like it should be 2, i've moved it back to that. I'll raise with design after I have a build
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like it should be 2, i've moved it back to that. I'll raise with design after I have a build
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behavior and design looks good to me, looking at the API update PR before commenting further on the implementation. Seems like it was rather straight forward which is good to see, were there any deviations from RSP TreeView's implementation that I may not have noticed?
// TODO: the below is needed so the borders of the top and bottom row isn't cut off if the TreeView is wrapped within a container by always reserving the 2px needed for the | ||
// keyboard focus ring. Perhaps find a different way of rendering the outlines since the top of the item doesn't | ||
// scroll into view due to how the ring is offset. Alternatively, have the tree render the top/bottom outline like it does in Listview | ||
const tree = style({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this comment still valid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm i assume, i copied it from v3 it looks like, i didn't think about it much though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fine if you want to merge and iterate
let {isExpanded, isDisabled} = fullProps; | ||
let {direction} = useLocale(); | ||
|
||
// Will need to keep the chevron as a button for iOS VO at all times since VO doesn't focus the cell. Also keep as button if cellAction is defined by the user in the future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why are we using hooks here? Why is it a span instead of a button?
borderInlineStartColor: borderColor, // don't know why i can't use renamed | ||
borderInlineEndColor: borderColor, | ||
borderTopColor: borderColor, | ||
borderBottomColor: borderColor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need these? When do the borders have different colors? Adding these will increase the CSS size across all components so want to be sure it's actually needed. Looks like maybe you could set the width of one of the borders to zero instead of setting it to transparent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using 0/1 instead of 1 always with transparent causes elements to shift their position internal to the tree item
@@ -670,6 +672,10 @@ export const style = createTheme({ | |||
borderEndWidth: createRenamedProperty('borderInlineEndWidth', borderWidth), | |||
borderTopWidth: borderWidth, | |||
borderBottomWidth: borderWidth, | |||
borderInlineStartColor: borderColor, // don't know why i can't use renamed | |||
borderInlineEndColor: borderColor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are keeping these then they should be named like our other logical properties: borderStartColor
, borderEndColor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, as the comment says, i don't know why I can't use the createRenamedProperty like I could for borderEndWidth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
## API Changes
react-aria-components/react-aria-components:UNSTABLE_Tree UNSTABLE_Tree <T extends {}> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean | FocusStrategy
children?: ReactNode | ({}) => ReactNode
className?: string | ((TreeRenderProps & {
defaultClassName: string | undefined
})) => string
defaultExpandedKeys?: Iterable<Key>
defaultSelectedKeys?: 'all' | Iterable<Key>
dependencies?: Array<any>
- disabledBehavior?: DisabledBehavior = 'selection'
+ disabledBehavior?: DisabledBehavior = 'all'
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
expandedKeys?: Iterable<Key>
id?: string
onAction?: (Key) => void
onExpandedChange?: (Set<Key>) => any
onScroll?: (UIEvent<Element>) => void
onSelectionChange?: (Selection) => void
renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
selectedKeys?: 'all' | Iterable<Key>
selectionBehavior?: SelectionBehavior
selectionMode?: SelectionMode
slot?: string | null
style?: CSSProperties | ((TreeRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
} /react-aria-components:TreeProps TreeProps <T> {
aria-describedby?: string
aria-details?: string
aria-label?: string
aria-labelledby?: string
autoFocus?: boolean | FocusStrategy
children?: ReactNode | (T) => ReactNode
className?: string | ((TreeRenderProps & {
defaultClassName: string | undefined
})) => string
defaultExpandedKeys?: Iterable<Key>
defaultSelectedKeys?: 'all' | Iterable<Key>
dependencies?: Array<any>
- disabledBehavior?: DisabledBehavior = 'selection'
+ disabledBehavior?: DisabledBehavior = 'all'
disabledKeys?: Iterable<Key>
disallowEmptySelection?: boolean
expandedKeys?: Iterable<Key>
id?: string
onAction?: (Key) => void
onExpandedChange?: (Set<Key>) => any
onScroll?: (UIEvent<Element>) => void
onSelectionChange?: (Selection) => void
renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
selectedKeys?: 'all' | Iterable<Key>
selectionBehavior?: SelectionBehavior
selectionMode?: SelectionMode
slot?: string | null
style?: CSSProperties | ((TreeRenderProps & {
defaultStyle: CSSProperties
})) => CSSProperties | undefined
} /react-aria-components:TreeItemContentRenderProps TreeItemContentRenderProps {
allowsDragging?: boolean
hasChildRows: boolean
+ id: Key
isDisabled: boolean
isDragging?: boolean
isDropTarget?: boolean
isExpanded: boolean
isFocusVisible: boolean
isFocusVisibleWithin: boolean
isFocused: boolean
isHovered: boolean
isPressed: boolean
isSelected: boolean
level: number
selectionBehavior: SelectionBehavior
selectionMode: SelectionMode
+ state: TreeState<unknown>
} @react-spectrum/s2/@react-spectrum/s2:TreeView+TreeView {
+ UNSAFE_className?: string
+ UNSAFE_style?: CSSProperties
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ autoFocus?: boolean | FocusStrategy
+ children?: ReactNode | (T) => ReactNode
+ defaultExpandedKeys?: Iterable<Key>
+ defaultSelectedKeys?: 'all' | Iterable<Key>
+ dependencies?: Array<any>
+ disabledBehavior?: DisabledBehavior = 'all'
+ disabledKeys?: Iterable<Key>
+ disallowEmptySelection?: boolean
+ expandedKeys?: Iterable<Key>
+ id?: string
+ isDetached?: boolean
+ isEmphasized?: boolean
+ items?: Iterable<T>
+ onAction?: (Key) => void
+ onExpandedChange?: (Set<Key>) => any
+ onSelectionChange?: (Selection) => void
+ renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
+ selectedKeys?: 'all' | Iterable<Key>
+ selectionMode?: SelectionMode
+ slot?: string | null
+ styles?: StylesPropWithHeight
+} /@react-spectrum/s2:TreeViewItem+TreeViewItem <T extends {}> {
+ aria-label?: string
+ childItems?: Iterable<{}>
+ children: ReactNode
+ download?: boolean | string
+ hasChildItems?: boolean
+ href?: Href
+ hrefLang?: string
+ id?: Key
+ onHoverChange?: (boolean) => void
+ onHoverEnd?: (HoverEvent) => void
+ onHoverStart?: (HoverEvent) => void
+ ping?: string
+ referrerPolicy?: HTMLAttributeReferrerPolicy
+ rel?: string
+ routerOptions?: RouterOptions
+ target?: HTMLAttributeAnchorTarget
+ textValue: string
+ value?: T
+} /@react-spectrum/s2:TreeViewProps+TreeViewProps {
+ UNSAFE_className?: string
+ UNSAFE_style?: CSSProperties
+ aria-describedby?: string
+ aria-details?: string
+ aria-label?: string
+ aria-labelledby?: string
+ autoFocus?: boolean | FocusStrategy
+ children?: ReactNode | (T) => ReactNode
+ defaultExpandedKeys?: Iterable<Key>
+ defaultSelectedKeys?: 'all' | Iterable<Key>
+ dependencies?: Array<any>
+ disabledBehavior?: DisabledBehavior = 'all'
+ disabledKeys?: Iterable<Key>
+ disallowEmptySelection?: boolean
+ expandedKeys?: Iterable<Key>
+ id?: string
+ isDetached?: boolean
+ isEmphasized?: boolean
+ items?: Iterable<T>
+ onAction?: (Key) => void
+ onExpandedChange?: (Set<Key>) => any
+ onSelectionChange?: (Selection) => void
+ renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
+ selectedKeys?: 'all' | Iterable<Key>
+ selectionMode?: SelectionMode
+ slot?: string | null
+ styles?: StylesPropWithHeight
+} /@react-spectrum/s2:TreeViewItemProps+TreeViewItemProps <T extends {} = {}> {
+ aria-label?: string
+ childItems?: Iterable<{}>
+ children: ReactNode
+ download?: boolean | string
+ hasChildItems?: boolean
+ href?: Href
+ hrefLang?: string
+ id?: Key
+ onHoverChange?: (boolean) => void
+ onHoverEnd?: (HoverEvent) => void
+ onHoverStart?: (HoverEvent) => void
+ ping?: string
+ referrerPolicy?: HTMLAttributeReferrerPolicy
+ rel?: string
+ routerOptions?: RouterOptions
+ target?: HTMLAttributeAnchorTarget
+ textValue: string
+ value?: T
+} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behavior for focusing the expand chevron seems to still work on iOS VO and Android talkback, and it is still properly skipped on keyboard. Only weird thing I saw in testing was that moving the talkback cursor caused the row to shift visibly but that seems to have been pre-exisiting since the virtualization commit. Not sure is causing that but happy for it to be followup since it is pretty low surface area
Closes
This only implements two of the combinations of isDetached/isEmphasized/Checkbox Selection.
The others aren't well defined enough yet to implement.
What's left:
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project: