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

[Popover ] Fixed incorrect element being focused when closed #2255

Merged
merged 2 commits into from
Nov 7, 2019

Conversation

AndrewMusgrave
Copy link
Member

@AndrewMusgrave AndrewMusgrave commented Oct 7, 2019

WHY are these changes introduced?

Fixes #2188

WHAT is this pull request doing?

  • When the popover is closed it'll focus the next focusable element. If a next focusable element is not present the activator will be focused
  • Create two focus utilities
  • Add tests for utilities
  • Add missing focus tests

How to 🎩

With another focusable element
import React, {useState, useCallback} from 'react';
import {Popover, Button, ActionList} from '../src';

export function Playground() {
  const [popoverActive, setPopoverActive] = useState(true);

  const togglePopoverActive = useCallback(
    () => setPopoverActive((popoverActive) => !popoverActive),
    [],
  );

  const activator = (
    <Button onClick={togglePopoverActive} disclosure>
      More actions
    </Button>
  );

  return (
    <div style={{height: '250px'}}>
      <Popover
        active={popoverActive}
        activator={activator}
        onClose={togglePopoverActive}
      >
        <ActionList items={[{content: 'Import'}, {content: 'Export'}]} />
      </Popover>
      <button>next focusable target</button>
    </div>
  );
}
Without another focusable element
import React, {useState, useCallback} from 'react';
import {Popover, Button, ActionList} from '../src';

export function Playground() {
  const [popoverActive, setPopoverActive] = useState(true);

  const togglePopoverActive = useCallback(
    () => setPopoverActive((popoverActive) => !popoverActive),
    [],
  );

  const activator = (
    <Button onClick={togglePopoverActive} disclosure>
      More actions
    </Button>
  );

  return (
    <div style={{height: '250px'}}>
      <Popover
        active={popoverActive}
        activator={activator}
        onClose={togglePopoverActive}
      >
        <ActionList items={[{content: 'Import'}, {content: 'Export'}]} />
      </Popover>
    </div>
  );
}

TODO

  • Add changelog

🎩 checklist

@github-actions
Copy link
Contributor

github-actions bot commented Oct 7, 2019

💦 Potential splash zone of changes introduced to src/**/*.tsx in this pull request:

Files modified10
Files potentially affected95

Details

All files potentially affected (total: 95)
📄 UNRELEASED.md (total: 0)

Files potentially affected (total: 0)

🧩 src/components/Popover/Popover.tsx (total: 23)

Files potentially affected (total: 23)

🧩 src/components/Popover/tests/Popover.test.tsx (total: 0)

Files potentially affected (total: 0)

🧩 src/components/Portal/Portal.tsx (total: 33)

Files potentially affected (total: 33)

🧩 src/components/Portal/tests/Portal.test.tsx (total: 0)

Files potentially affected (total: 0)

🧩 src/components/shared.ts (total: 93)

Files potentially affected (total: 93)

🧩 src/utilities/focus.ts (total: 65)

Files potentially affected (total: 65)

🧩 src/utilities/is-element-in-viewport.ts (total: 66)

Files potentially affected (total: 66)

🧩 src/utilities/tests/focus.test.ts (total: 0)

Files potentially affected (total: 0)

🧩 src/utilities/tests/is-element-in-viewport.test.ts (total: 0)

Files potentially affected (total: 0)


This comment automatically updates as changes are made to this pull request.
Feedback, troubleshooting: open an issue or reach out on Slack in #polaris-tooling.

@AndrewMusgrave AndrewMusgrave force-pushed the popover-focus-next branch 2 times, most recently from 1717d92 to 6d89db1 Compare October 7, 2019 22:15
let nextElement = node.nextElementSibling;

while (nextElement) {
if (nextElement.matches(FOCUSABLE_SELECTOR)) return nextElement;
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'm going with the matches approach that rather a tabIndex so we can ignore disabled elements

while (nextElement) {
if (nextElement.matches(FOCUSABLE_SELECTOR)) return nextElement;

if (nextElement instanceof HTMLElement) {
Copy link
Member Author

Choose a reason for hiding this comment

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

findFirstFocusableNode require HTMLElement rather than Element. Although nextElementSibling won't return text nodes so I'm not sure we'll ever get an Element


export function focusNextFocusableNode(node: HTMLElement) {
const nextFocusable = nextFocusableNode(node);
if (nextFocusable && nextFocusable instanceof HTMLElement) {
Copy link
Member Author

Choose a reason for hiding this comment

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

The Element interface doesn't contain focus so we need this check

const nextFocusable = nextFocusableNode(node);
if (nextFocusable && nextFocusable instanceof HTMLElement) {
nextFocusable.focus();
return true;
Copy link
Member Author

Choose a reason for hiding this comment

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

Added a boolean return rather than void so we can determine if an element was focused or not

@AndrewMusgrave AndrewMusgrave marked this pull request as ready for review October 7, 2019 22:45
export function nextFocusableNode(
node: HTMLElement,
): HTMLElement | Element | null {
let nextElement = node.nextElementSibling;
Copy link
Contributor

Choose a reason for hiding this comment

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

This logic will only work if the nextFocusableElement same tree level or lower correct? The following would not pass

 <React.Fragment>
    <div><Popover active activator={<div />} onClose={noop} /></div>
     <button id={id} />
</React.Fragment>

I think it's fine since the activator will get focus in the end. I can't think of an inexpensive way to do this either. What do you think?

@dleroux dleroux self-requested a review October 17, 2019 12:39
Copy link
Contributor

@dpersing dpersing left a comment

Choose a reason for hiding this comment

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

I'm not sure if this is a new issue or not (since our previous examples didn't have additional elements to manage focus around, but I found while testing this that focus is does not appear to be moved to the popover on iOS when the button is activated. When using VoiceOver with Safari or Chrome on iOS, I had to swipe to the next focusable target button before I could swipe to the popover contents. Can we confirm if this was an existing issue, or if it's introduced by this update? If it's not new, I'll open a new issue, which we should address as soon as possible, since it impacts popover usage across mobile.

I wonder if we can also take a look at web to see if anyone is overriding our current focus management logic for popovers, to make sure this update won't cause conflicts?

@AndrewMusgrave AndrewMusgrave force-pushed the popover-focus-next branch 3 times, most recently from 0ba6a36 to 4b255bb Compare October 22, 2019 18:53
@AndrewMusgrave
Copy link
Member Author

I'm not sure if this is a new issue or not (since our previous examples didn't have additional elements to manage focus around, but I found while testing this that focus is does not appear to be moved to the popover on iOS when the button is activated. When using VoiceOver with Safari or Chrome on iOS, I had to swipe to the next focusable target button before I could swipe to the popover contents. Can we confirm if this was an existing issue, or if it's introduced by this update? If it's not new, I'll open a new issue, which we should address as soon as possible, since it impacts popover usage across mobile.

@dleroux and I spent some time on iOS voiceover and it appears it's an existing issue. Oddly enough we still have the correct focus order, however it seems voice was following the dom order, and not respecting the currently focused element. Is this many a voiceover bug?

I wonder if we can also take a look at web to see if anyone is overriding our current focus management logic for popovers, to make sure this update won't cause conflicts?

It would be pretty difficult to check all the popovers since there're hundreds, maybe thousands 😬 however I tested home, and a few other pages as well and everything looked 👍

@dpersing
Copy link
Contributor

Created issue #2387 to capture the issue with VoiceOver on iOS.

@dleroux
Copy link
Contributor

dleroux commented Nov 4, 2019

Is this ready to review again @AndrewMusgrave?

@dpersing
Copy link
Contributor

dpersing commented Nov 4, 2019

@dleroux Per discussion in Slack last week, yes!

Copy link
Contributor

@dleroux dleroux left a comment

Choose a reason for hiding this comment

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

🎩 and works great.

Interestingly enough as I was tabbing through the popover I would have expected it to trap focus but I'm guessing that is not the case? @dpersing

node: HTMLElement,
filter?: Filter,
): HTMLElement | Element | null {
const allFocusableElements = [
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

});
});

function domSetup(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great, not asking to change it, but I wonder if it wouldn't have been simpler outline the dom being tested with each test. It makes it a little tricky to review and it's almost like domSetup() needs its own tests.

@AndrewMusgrave AndrewMusgrave merged commit b1127fa into master Nov 7, 2019
@AndrewMusgrave AndrewMusgrave deleted the popover-focus-next branch November 7, 2019 19:31
@PabloVallejo PabloVallejo temporarily deployed to production November 12, 2019 14:48 Inactive
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.

[Popover] Keyboard navigation enhancements
4 participants