Skip to content
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: Tabs collapse behaviour #7202

Merged
merged 25 commits into from
Dec 2, 2024
Merged

feat: Tabs collapse behaviour #7202

merged 25 commits into from
Dec 2, 2024

Conversation

snowystinger
Copy link
Member

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@rspbot
Copy link

rspbot commented Oct 17, 2024

@snowystinger snowystinger changed the title [WIP] feat: Tabs collapse behaviour feat: Tabs collapse behaviour Oct 21, 2024
@snowystinger snowystinger marked this pull request as ready for review October 21, 2024 04:53
@rspbot
Copy link

rspbot commented Oct 21, 2024

@rspbot
Copy link

rspbot commented Oct 22, 2024

@rspbot
Copy link

rspbot commented Oct 22, 2024

Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review, will finish Monday

packages/@react-spectrum/s2/stories/Tabs.stories.tsx Outdated Show resolved Hide resolved
packages/@react-spectrum/s2/src/Tabs.tsx Outdated Show resolved Hide resolved
packages/@react-spectrum/s2/src/Tabs.tsx Outdated Show resolved Hide resolved
let tab: HTMLElement | null = tablistRef.current.querySelector('[role=tab][data-selected=true]');

if (tab != null) {
setSelectedTab(tab);
}
} else if (tablistRef?.current) {
let picker: HTMLElement | null = tablistRef.current.querySelector('button');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget if aria disallows it, but is the picker guaranteed to be the only button element in the tablist? I don't see children presentation for the tablist in the spec

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the role tablist doesn't allow for anything other than tabs, I may need to switch the role when we collapse now that you bring it up...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just looked at S1 Tabs, looks like we don't change the role of the picker but I see in https://w3c.github.io/aria/#tablist does explicitly state "tab" as the only allowed child role like you mentioned... cc @majornista

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@majornista thoughts?

packages/@react-spectrum/s2/src/Tabs.tsx Show resolved Hide resolved
@rspbot
Copy link

rspbot commented Nov 4, 2024

@rspbot
Copy link

rspbot commented Nov 4, 2024

# Conflicts:
#	packages/@react-spectrum/s2/stories/Tabs.stories.tsx
@rspbot
Copy link

rspbot commented Nov 20, 2024

# Conflicts:
#	packages/@react-spectrum/s2/package.json
#	yarn.lock
@rspbot
Copy link

rspbot commented Nov 21, 2024

reidbarber
reidbarber previously approved these changes Nov 22, 2024
Copy link
Member

@reidbarber reidbarber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

# Conflicts:
#	packages/@react-spectrum/s2/src/Tabs.tsx
@rspbot
Copy link

rspbot commented Nov 27, 2024

reidbarber
reidbarber previously approved these changes Nov 27, 2024
LFDanLu
LFDanLu previously approved these changes Nov 27, 2024
Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving for testing, happy for the rest to be followup

Comment on lines +485 to +486
// need to determine the best way to handle icon only -> icon and text
// good enough to aria-label the picker item?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interesting situation can happen with the icon only tabs: if you collapse https://reactspectrum.blob.core.windows.net/reactspectrum/777f4b761ccd6a3e45cbdd9d8ee4d623ee6e3516/storybook-s2/index.html?path=/story/tabs--icons&providerSwitcher-locale=he-IL, you will never be able to see the full contents of two of the options because the point of collapse for the icon only tabs is too small. This maybe partially mitigated by the work you are doing to support tooltips on collection items, but maybe this means we should only render the icon of the tab even when it is collapsed but display the full text in the menu dropdown like it already does. Alternatively, we could instead just have the text truncate so it doesn't get cut off

Happy for this to be followup and for others to weigh in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow, are you talking about how the picker shows the label and runs off the overflow area? So the picker should only have the icon?
here's the two options I was trying to decide between
Screenshot 2024-11-29 at 8 50 50 am
Screenshot 2024-11-29 at 8 53 59 am
Screenshot 2024-11-29 at 8 54 22 am

The reason I went the direction I did was so it would more easily label, but I could change it to have the display none text aria-label the icon possibly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you basically got the gist of it, essentially the user doesn't have a great way of seeing the selected tab's full text other than opening the dropdown. IMO option 2 that you show above isn't too bad for sighted users since the non collapsed tabs already didn't show the tab's text and aria-labeling would work for screen reader users.

);
}

function isAllTabsDisabled<T>(collection: Collection<Node<T>> | undefined, disabledKeys: Set<Key>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that the selectionManager/collection can't help in a case like this where the individual items might be disabled. Maybe worth making RAC tabs return this info in the render props or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm possibly
yeah, the selectionManager is unaware of it, the collection could do it, but I'm not sure if it should because 'all' is a little subjective to the collection being async or not

it'd be more like, 'all currently included items disabled' or something

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, perhaps something we can discuss/handle as a follow up if others using RAC tabs find themselves needing similar logic

packages/@react-spectrum/s2/src/TabsPicker.tsx Outdated Show resolved Hide resolved
packages/@react-spectrum/s2/src/TabsPicker.tsx Outdated Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't notice this before, but it seems like focus is lost if you keyboard select an option from the tab picker menu. Also getting warning about missing a heading in the console

Copy link
Member Author

@snowystinger snowystinger Nov 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh... i swear i tested this, however, it looks broken in every commit since I added the picker, so not sure what happened. looking into it

focusscope thinks it's restoring to the correct element, the trigger remains in the dom...

ah, react-aria-focus-scope-restore is preventing default off in useSelectableCollection for the tablist, but it doesn't know how to focus the picker

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i've addressed it in Tabs, not sure if it should be addressed further down

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh interesting, doesn't feel like useSelectableCollection should be preventing default in this case. Might be the same issue mentioned here: #6451 (comment), lets discuss in a follow up

yihuiliao
yihuiliao previously approved these changes Nov 27, 2024
Copy link
Member

@yihuiliao yihuiliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small comments which can be follow-up

packages/@react-spectrum/s2/src/Tabs.tsx Outdated Show resolved Hide resolved
packages/@react-spectrum/s2/src/TabsPicker.tsx Outdated Show resolved Hide resolved
@snowystinger snowystinger dismissed stale reviews from yihuiliao, LFDanLu, and reidbarber via 5caf3ea November 27, 2024 23:41
@rspbot
Copy link

rspbot commented Nov 29, 2024

@rspbot
Copy link

rspbot commented Nov 29, 2024

## API Changes

@react-spectrum/s2

/@react-spectrum/s2:Tabs

 Tabs {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   defaultSelectedKey?: Key
   density?: 'compact' | 'regular' = 'regular'
   disabledKeys?: Iterable<Key>
+  iconOnly?: boolean
   id?: string
   isDisabled?: boolean
   keyboardActivation?: 'automatic' | 'manual' = 'automatic'
   onSelectionChange?: (Key) => void
   selectedKey?: Key | null
   slot?: string | null
   styles?: StylesPropWithHeight
 }

/@react-spectrum/s2:TabList

 TabList <T extends {}> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
-  children?: ReactNode
+  children?: ReactNode | (T) => ReactNode
   dependencies?: Array<any>
   items?: Iterable<T>
   styles?: StylesProp
 }

/@react-spectrum/s2:Tab

 Tab {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
-  children?: ReactNode
+  children: ReactNode
   download?: boolean | string
   href?: Href
   hrefLang?: string
   id?: Key
   onHoverChange?: (boolean) => void
   onHoverEnd?: (HoverEvent) => void
   onHoverStart?: (HoverEvent) => void
   ping?: string
   referrerPolicy?: HTMLAttributeReferrerPolicy
   rel?: string
   routerOptions?: RouterOptions
   styles?: StylesProp
   target?: HTMLAttributeAnchorTarget
 }

/@react-spectrum/s2:TabsProps

 TabsProps {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   defaultSelectedKey?: Key
   density?: 'compact' | 'regular' = 'regular'
   disabledKeys?: Iterable<Key>
+  iconOnly?: boolean
   id?: string
   isDisabled?: boolean
   keyboardActivation?: 'automatic' | 'manual' = 'automatic'
   onSelectionChange?: (Key) => void
   selectedKey?: Key | null
   slot?: string | null
   styles?: StylesPropWithHeight
 }

/@react-spectrum/s2:TabProps

 TabProps {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
-  children?: ReactNode
+  children: ReactNode
   download?: boolean | string
   href?: Href
   hrefLang?: string
   id?: Key
   onHoverChange?: (boolean) => void
   onHoverEnd?: (HoverEvent) => void
   onHoverStart?: (HoverEvent) => void
   ping?: string
   referrerPolicy?: HTMLAttributeReferrerPolicy
   rel?: string
   routerOptions?: RouterOptions
   styles?: StylesProp
   target?: HTMLAttributeAnchorTarget
 }

/@react-spectrum/s2:TabListProps

 TabListProps <T> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
-  children?: ReactNode
+  children?: ReactNode | (T) => ReactNode
   dependencies?: Array<any>
   items?: Iterable<T>
   styles?: StylesProp
 }

@LFDanLu LFDanLu added this pull request to the merge queue Dec 2, 2024
Merged via the queue into main with commit cd4da2b Dec 2, 2024
30 checks passed
@LFDanLu LFDanLu deleted the tabs-collapse branch December 2, 2024 18:54
LFDanLu added a commit that referenced this pull request Jan 11, 2025
github-merge-queue bot pushed a commit that referenced this pull request Jan 13, 2025
* Revert "chore: Update JSDocs and apis for release (#7595)"

This reverts commit 101d077.

* Revert "fix: Tabs infinite loop (#7487)"

This reverts commit 8228e4e.

* Revert "fix: Tabs from testing (#7463)"

This reverts commit 494e01c.

* Revert "feat: Tabs collapse behaviour (#7202)"

This reverts commit cd4da2b.

* add stuff back that we still want

* update lock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants