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

Remove activedescendant focus pattern from SelectPanel #5688

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

camertron
Copy link
Contributor

Closes https://github.com/github/primer/issues/4740

Changelog

New

Changed

  1. Remove the activedescendant focus pattern from SelectPanel in favor of regular 'ol focus behavior with a roving tab index.

Removed

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

  • Added/updated tests
    - [ ] Added/updated documentation
    - [ ] Added/updated previews (Storybook)
  • Changes are SSR compatible
  • Tested in Chrome
  • Tested in Firefox
  • Tested in Safari
  • Tested in Edge
  • (GitHub staff only) Integration tests pass at github/github (Learn more about how to run integration tests)

Copy link

changeset-bot bot commented Feb 11, 2025

🦋 Changeset detected

Latest commit: df77160

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Feb 11, 2025
Copy link
Contributor

👋 Hi, this pull request contains changes to the source code that github/github depends on. If you are GitHub staff, we recommend testing these changes with github/github using the integration workflow. Thanks!

@@ -243,7 +205,6 @@ function MappedActionListItem(item: ItemInput & {renderItem?: RenderItemFn}) {

return (
<ActionList.Item
role="option"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The role is automatically inferred by ActionList.

@github-actions github-actions bot requested a deployment to storybook-preview-5688 February 11, 2025 22:50 Abandoned
className={enabled ? clsx(className, classes.FilteredActionList) : className}
announcementsEnabled={false}
/>
<ActionListContainerContext.Provider value={{container: 'SelectPanel', focusZoneFocusOutBehavior: 'wrap'}}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is only for the modern ActionList and shouldn't affect the deprecated one.

@@ -253,7 +253,6 @@ const StyledItem = styled.div<

&:focus {
background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg};
outline: none;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Allows items to appear visually focused.

Copy link
Contributor

github-actions bot commented Feb 12, 2025

size-limit report 📦

Path Size
packages/react/dist/browser.esm.js 105.18 KB (-0.34% 🔽)
packages/react/dist/browser.umd.js 105.54 KB (-0.32% 🔽)

@primer-integration
Copy link

👋 Hi from github/github! Your integration PR is ready: https://github.com/github/github/pull/362191

@github-actions github-actions bot temporarily deployed to storybook-preview-5688 February 12, 2025 20:28 Inactive
@primer primer bot temporarily deployed to github-pages February 12, 2025 20:40 Inactive
@github-actions github-actions bot temporarily deployed to storybook-preview-5688 February 12, 2025 20:41 Inactive
@github-actions github-actions bot added integration-tests: failing Changes in this PR cause breaking changes in gh/gh integration-tests: passing Changes in this PR do NOT cause breaking changes in gh/gh and removed integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm integration-tests: failing Changes in this PR cause breaking changes in gh/gh labels Feb 13, 2025
@camertron camertron marked this pull request as ready for review February 13, 2025 21:32
@camertron camertron requested a review from a team as a code owner February 13, 2025 21:32
@camertron camertron requested a review from a team as a code owner February 13, 2025 21:32
@camertron camertron requested review from maximedegreve, jonrohan and francinelucca and removed request for jonrohan and maximedegreve February 13, 2025 21:32
@primer-integration
Copy link

🟢 golden-jobs completed with status success.

Copy link
Member

@francinelucca francinelucca left a comment

Choose a reason for hiding this comment

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

Looking great! just a question on the new FilterActionList custom attribute

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Feb 14, 2025
Copy link
Contributor

👋 Hi, there are new commits since the last successful integration test. We recommend running the integration workflow once more, unless you are sure the new changes do not affect github/github. Thanks!

@github-actions github-actions bot added integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm and removed integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm labels Feb 14, 2025
Copy link
Contributor

👋 Hi, there are new commits since the last successful integration test. We recommend running the integration workflow once more, unless you are sure the new changes do not affect github/github. Thanks!

@github-actions github-actions bot added integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm and removed update snapshots integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm labels Feb 14, 2025
Copy link
Contributor

👋 Hi, there are new commits since the last successful integration test. We recommend running the integration workflow once more, unless you are sure the new changes do not affect github/github. Thanks!

Copy link
Member

@TylerJDev TylerJDev left a comment

Choose a reason for hiding this comment

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

Took a look and everything's looking good! Will follow up in my next review to test with a screen reader!

Comment on lines +151 to +157
items={items.map(item => {
return {
...item,
'data-select-panel-item': container === 'SelectPanel',
role: 'option',
}
})}
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 utilize useMemo here? I noticed that this was being called a few times on the initial render. I think if we utilize useMemo we could reduce it to one since we'll memorize the array.

const filteredItems: ItemInput[] = React.useMemo(() => {
  return items.map(item => {
    return {
      ...item,
      'data-select-panel-item': container === 'SelectPanel',
      role: 'option',
    }
  })
}, [items, container])

Not a big concern though, so up to you! I believe this will still be called upon filtering existing options, so this only helps us when it comes to the initial render.

@@ -0,0 +1,5 @@
---
"@primer/react": patch
Copy link
Member

Choose a reason for hiding this comment

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

Nit: I'm wondering if this should be minor instead of patch. Entirely up to you though! 😁

Comment on lines 174 to +187
function MappedActionListItem(item: ItemInput & {renderItem?: RenderItemFn}) {
const {container} = useContext(ActionListContainerContext)

// keep backward compatibility for renderItem
// escape hatch for custom Item rendering
if (typeof item.renderItem === 'function') return item.renderItem(item)
if (typeof item.renderItem === 'function') {
if (container === 'SelectPanel') {
return React.cloneElement(item.renderItem(item), {
'data-select-panel-item': true,
})
}

return item.renderItem(item)
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we have an example that utilizes this? Not a blocker, but just curious 🤔

Comment on lines -33 to -51
const getItemWithActiveDescendant = (
listRef: React.RefObject<HTMLElement>,
items: FilteredActionListProps['items'],
) => {
const listElement = listRef.current
const activeItemElement = listElement?.querySelector('[data-is-active-descendant]')

if (!listElement || !activeItemElement?.textContent) return

const optionElements = listElement.querySelectorAll('[role="option"]')

const index = Array.from(optionElements).indexOf(activeItemElement)
const activeItem = items[index] as ItemInput | undefined

const text = activeItem?.text
const selected = activeItem?.selected

return {index, text, selected}
}
Copy link
Member

Choose a reason for hiding this comment

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

Wow, active descendant used a lot of code! It's great that we're able to clean it all up 🥹

Comment on lines +163 to +167
useEffect(() => {
if (open && inputRef) {
inputRef.current?.focus()
}
}, [inputRef, open])
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 potentially add the .focus() inside of onInputRefChanged, as this appears to be where we get the input ref initially, and avoids using useEffect.

Though I'm not 100% on when onInputRefChanged is called. It seems like only when the ref is set, so should be initial render? 🤔

Copy link
Member

Choose a reason for hiding this comment

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

It looks like we already focus it even without the .focus() we provide here. I'm wondering if this is because the container (AnchoredOverlay) automatically focuses the first focusable element?

I think this is set via the following on (currently) line 357:

const focusTrapSettings = {
  initialFocusRef: inputRef || undefined,
}

We might be able to omit the useEffect entirely, (and my previous comment 😅)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration-tests: passing Changes in this PR do NOT cause breaking changes in gh/gh integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants