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: Adds single tab stop util #111

Closed
wants to merge 5 commits into from
Closed

Conversation

katiegeorge
Copy link
Member

@katiegeorge katiegeorge commented Jan 13, 2025

Issue #, if available:

Description of changes:
This PR adds two new utils: SingleTabStop (in the form of useSingleTabStopNavigation, SingleTabStopProvider and SingleTabStopNavigationAPI), and SingleTabStopKeyboardNav.

SingleTabStop is currently being use in the components repository inside Tabs, Table (for grid navigation), and Button Group.

It needs to be added here instead due to the same utility being needed for the upcoming SupportPromptGroup release. SupportPromptGroup is in the chat-components repository.

SingleTabStopKeyboardNav can be reused inside components which use uni-directional keyboard navigation, currently Tabs and ButtonGroup (and soon, SupportPromptGroup).


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Copy link

codecov bot commented Jan 15, 2025

Codecov Report

Attention: Patch coverage is 42.64706% with 39 lines in your changes missing coverage. Please review.

Project coverage is 93.88%. Comparing base (5ae5804) to head (aea0f04).

Files with missing lines Patch % Lines
src/internal/single-tab-stop/index.tsx 33.33% 38 Missing ⚠️
src/dom/node-belongs.ts 90.90% 1 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (5ae5804) and HEAD (aea0f04). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (5ae5804) HEAD (aea0f04)
2 1
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #111      +/-   ##
==========================================
- Coverage   99.23%   93.88%   -5.35%     
==========================================
  Files          27       29       +2     
  Lines         652      720      +68     
  Branches      173      193      +20     
==========================================
+ Hits          647      676      +29     
- Misses          5       44      +39     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@katiegeorge katiegeorge changed the title wip: single tab stop feat: Adds single tab stop util Jan 15, 2025
@katiegeorge katiegeorge marked this pull request as ready for review January 15, 2025 21:21
@katiegeorge katiegeorge requested a review from a team as a code owner January 15, 2025 21:21
@katiegeorge katiegeorge requested review from georgylobko and pan-kot and removed request for a team January 15, 2025 21:21
focus(): void;
}

export default function useForwardFocus(
Copy link
Member

Choose a reason for hiding this comment

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

Why this util is needed here? I see it is only used in a button from test page, but not sure why.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call, I copied it over from button group, but seems it is not needed here.

* @param container Container node
* @param target Node that is checked to be a descendant of the container
*/
export function nodeBelongs(container: Node | null, target: Node | EventTarget | null): boolean {
Copy link
Member

Choose a reason for hiding this comment

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

let's take an action to reuse the nodeBelongs, handleKey, isNode, and other utils that are now part of the component-toolkit in the components and, potentially, other repos.


export default function SingleTabStopKeyboardNav({
children,
matchId,
Copy link
Member

Choose a reason for hiding this comment

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

the matchId name is not very telling - it is actually a selector that finds interactive elements that can be focus targets

@@ -0,0 +1,48 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

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

Should we call this focus utils instead? This file lives in the focus-lock folder, but we did not carry over the focus lock component to the toolkit.

function getNextFocusTarget(): null | HTMLElement {
if (containerObjectRef.current) {
const buttons: HTMLButtonElement[] = Array.from(containerObjectRef.current.querySelectorAll(matchId));
return buttons.find(button => button.dataset.itemid === focusedIdRef.current) ?? buttons[0] ?? null;
Copy link
Member

Choose a reason for hiding this comment

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

This integration is rather quite implicit: the interactive elements must have the data-itemid and must be all assigned a selector. I wonder if this can be simplified. For instance, here we can find all interactive elements using getAllFocusables and then instead of matchId expose some matchFocusTarget(focusedItemId, focusableElements): null | Element - this way the matching can be performed on the consumer end and can use any mechanism so not relying on assigning the specific data- attribute.

import { KeyCode } from '../../../src/internal/keycode';
import { getAllFocusables } from '../../../src/internal/focus-lock/utils';

export default function SingleTabStopKeyboardNav({
Copy link
Member

Choose a reason for hiding this comment

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

Would it make more sense to introduce this util in the components first to ensure the button group and tabs can actually use it? Meanwhile, a PR to the toolkit can still carry over the necessary internal utils.

Copy link
Member Author

Choose a reason for hiding this comment

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

It does work with button group. Tabs is unfortunately a bit more complicated and I'm not sure it would make sense to carry this over there.

We can, alternatively, not pull out the KeyboardNav util and just reuse that portion in both button group and support prompt group for now.

@katiegeorge
Copy link
Member Author

Closed in favor of #112

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.

2 participants