Skip to content

Conversation

@so99ynoodles
Copy link
Contributor

@so99ynoodles so99ynoodles commented Jul 24, 2020

Closes #784

  • Implement useTabs
  • Implement useTab
  • Add TabsKeyboardDelegate
  • Change on useSelectableList or create useTabState

✅ 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:

  • Added dummy storybook / components those are using useTabs / useTab at react-spectrum. But there is a problem I'm getting stuck on. You can ignore those components since they are for demo.
  • I'll write some tests if they are necessary, mainly they will be the Keyboard.

🧢 Your Project:

@so99ynoodles so99ynoodles changed the title [WIP] Add useTabs / Tabs Component Add useTabs / Tabs Component Jul 24, 2020
}


export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
Copy link
Member

Choose a reason for hiding this comment

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

looks like this delegate won't do wrapping, should tabs keyboard navigation do wrapping? @devongovett do you know the answer to this?
If so, you can probably borrow the logic from ActionButtons, https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/actiongroup/src/ActionGroupKeyboardDelegate.ts

Copy link
Member

@LFDanLu LFDanLu Jul 24, 2020

Choose a reason for hiding this comment

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

+1 to the above. Looking at https://www.w3.org/TR/wai-aria-practices-1.2/#keyboard-interaction-20, focus wrapping should be supported IMO. The ActionGroupKeyboardDelegate logic will also solve the direction flipping that happens in RTL locales

Copy link
Member

Choose a reason for hiding this comment

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

I don't think that needs to be up to the delegate. We have the shouldFocusWrap option to useSelectableList that should handle it.

Copy link
Member

Choose a reason for hiding this comment

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

we could also consider merging this into ListKeyboardDelegate since it's pretty similar. We'd need to add the orientation option, as well as options to disable typeahead and page up/page down I guess.

Copy link
Member

Choose a reason for hiding this comment

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

That should work too, I guess we would need to add the existing shouldFocusWrap logic in useSelectableCollection to ArrowLeft and ArrowRight right?

Copy link
Member

Choose a reason for hiding this comment

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

@so99ynoodles If I am interpreting @devongovett comment correctly (feel free to correct me if I am misunderstanding @devongovett ) I think the focus wrapping should solely be handled by useSelectableCollection via the shouldFocusWrap option. As of now, this keyboard delegate actually handles focus wrapping independently of useSelectableCollection due to how getNextKey and getPreviousKey work (namely

if (!key) {
key = this.collection.getFirstKey();
}
and
if (!key) {
key = this.collection.getLastKey();
}
).

Thinking out loud, I think the next change would be to do the following:

  1. remove getNextKey and getPreviousKey from TabsKeyboardDelegate
  2. replace this.getNextKey(key) with this.collection.getKeyAfter(key) and this.getPreviousKey(key) with `this.collection.getKeyBefore(key)
  3. Add the same shouldFocusWrap logic that exists in useSelectableCollection for ArrowUp and ArrowDown to ArrowLeft and ArrowRight
  4. Ugh, some of the directional stuff gets hairy with getFirstKey and getLastKey here, perhaps useSelectableCollection needs flipDirection communicated to it? I'll need to think about this more

Copy link
Member

Choose a reason for hiding this comment

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

Heh, well... appears all that was copied from ActionGroupKeyboardDelegate so we'd need to change that one too. Perhaps this could be done as a separate task from this PR? Combine all the keyboard delegates into ListKeyboardDelegate.

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.

Added some comments regarding the aria hook parts of your code. Can comment on the spectrum component parts if you are interested but figured we should focus on the aria bits first. Great work so far btw :)

}


export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think that needs to be up to the delegate. We have the shouldFocusWrap option to useSelectableList that should handle it.

}


export class TabsKeyboardDelegate<T> implements KeyboardDelegate {
Copy link
Member

Choose a reason for hiding this comment

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

we could also consider merging this into ListKeyboardDelegate since it's pretty similar. We'd need to add the orientation option, as well as options to disable typeahead and page up/page down I guess.

@so99ynoodles
Copy link
Contributor Author

so99ynoodles commented Jul 25, 2020

@devongovett @LFDanLu @snowystinger
Thank you for reviewing! 😄 I really appreciate it.

Fixed

  • Add @react-types/tabs and fix some props.
  • Combine TabList to Tabs component and move Tab component to the same file.
  • Replicate selectOnFocus logic of useSelectableCollection under the shouldFocusWrap if statement.
  • Fix TabsKeyboardDelegate logic. (merging it to ListKeyboardDelegate is also considerable, I thought having independent file is simpler.)
  • Fix useTabs to provide tabPanelProps instead of useTab.
  • Create util that generate id for tab and tabpanel.

Testing (Want advice)

  • Add Picker component to implement overflowMode: dropdown
    • Picker calls useSelectState inside, so it uses similar hooks multiple times now...

Comment on lines 56 to 64
useEffect(() => {
let tabs: HTMLElement[] = Array.from(ref.current.querySelectorAll('.' + styles['spectrum-Tabs-item']));
if (overflowMode === 'scrolling') {
setSelectedTab(tabs.find(tab => tab.dataset.key === state.selectedKey));
}
if (overflowMode === 'dropdown') {
setSelectedTab(tabs[0]);
}
}, [props.children, state.selectedKey, overflowMode]);
Copy link
Member

@LFDanLu LFDanLu Jul 27, 2020

Choose a reason for hiding this comment

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

With regards to the overflow mode you've implemented here is some code that might be of interest: https://github.com/adobe/react-spectrum/blob/main/packages/@react-spectrum/breadcrumbs/src/Breadcrumbs.tsx#L56-L120. Breadcrumbs does some overflow handling that maybe of use. Not a hard requirement for this pull, just putting it here if you are interested, you can play around with how it works here: https://react-spectrum.adobe.com/react-spectrum/Breadcrumbs.html#visible-items-overflow-behavior

Copy link
Member

Choose a reason for hiding this comment

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

Tabs is a bit different though. As soon as any tab doesn't fit, then all tabs collapse into a dropdown, whereas with breadcrumbs, they collapse one by one. We'll need to verify this behavior with the Spectrum design team though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll remove this from this PR and cover it in another PR! Thank you.

Copy link
Member

@devongovett devongovett left a comment

Choose a reason for hiding this comment

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

Looking good! 😄

Comment on lines 56 to 64
useEffect(() => {
let tabs: HTMLElement[] = Array.from(ref.current.querySelectorAll('.' + styles['spectrum-Tabs-item']));
if (overflowMode === 'scrolling') {
setSelectedTab(tabs.find(tab => tab.dataset.key === state.selectedKey));
}
if (overflowMode === 'dropdown') {
setSelectedTab(tabs[0]);
}
}, [props.children, state.selectedKey, overflowMode]);
Copy link
Member

Choose a reason for hiding this comment

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

Tabs is a bit different though. As soon as any tab doesn't fit, then all tabs collapse into a dropdown, whereas with breadcrumbs, they collapse one by one. We'll need to verify this behavior with the Spectrum design team though.

@devongovett
Copy link
Member

@so99ynoodles awesome work! What do you think about merging an initial implementation and then following up in separate PRs for additional features like overflow mode? Seems like that could help keep the PR to a good size.

The next step would be adding some tests. We use react-testing-library and Jest. You can check out some of the existing tests for other components to see how they're written. They don't have to be 100% complete for the first merge, but it would be good to start with a few basic ones. Let us know if you have questions!

@so99ynoodles so99ynoodles requested a review from devongovett July 28, 2020 09:44
@so99ynoodles
Copy link
Contributor Author

@devongovett
I think it's a nice idea to separate PR!

The next step would be adding some tests

Do you want me to add tests to this PR?

@devongovett
Copy link
Member

@so99ynoodles a few tests to cover the basics would be great! We can add more later on.

@so99ynoodles
Copy link
Contributor Author

@devongovett
Added some tests!

Copy link
Member

@devongovett devongovett left a comment

Choose a reason for hiding this comment

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

Looks great! Thanks so much. 😄

@devongovett devongovett merged commit 48fa5dd into adobe:main Jul 31, 2020
@darekkay
Copy link

Hey @devongovett ,
I am missing the Tabs component from the Adobe React Spectrum demo page. Are there any plans to include it?

@devongovett
Copy link
Member

@darekkay its still in alpha right now, but we will have docs once a stable versions ships (hopefully soon!).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a tab component to react-spectrum

5 participants