Skip to content

Commit

Permalink
[ButtonBase] Update focusVisible polyfill for shadowRoots (#13447)
Browse files Browse the repository at this point in the history
The focusVisible polyfill for ButtonBase components was not resolving
targets inside a shadowRoot. Now the activeElement for nested
shadowRoots is resolved to add focus visible classes to targets in the
shadow DOM.

Closes #13447
  • Loading branch information
JaiPe committed Nov 1, 2018
1 parent 979346a commit d18ae71
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
45 changes: 45 additions & 0 deletions packages/material-ui/src/ButtonBase/ButtonBase.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,51 @@ describe('<ButtonBase />', () => {
});
});

describe('focus inside shadowRoot', () => {
let wrapper;
let instance;
let button;
let clock;

beforeEach(() => {
clock = useFakeTimers();
const rootElement = document.createElement('div');
rootElement.tabIndex = 0;
rootElement.attachShadow({ mode: 'open' });
wrapper = mount(
<ButtonBaseNaked theme={{}} classes={{}} id="test-button">
Hello
</ButtonBaseNaked>,
);
instance = wrapper.instance();
button = document.getElementById('test-button');
if (!button) {
throw new Error('missing button');
}

wrapper.simulate('focus');
rootElement.focus();

// Mock activeElement value in shadow root due to lack of support in [email protected]
// https://github.com/jsdom/jsdom/issues/2343
rootElement.shadowRoot.activeElement = button;

const event = new window.Event('keyup');
event.which = keycode('tab');
window.dispatchEvent(event);
});

afterEach(() => {
clock.restore();
});

it('should set focus state for shadowRoot children', () => {
assert.strictEqual(wrapper.state().focusVisible, false);
clock.tick(instance.focusVisibleCheckTime * instance.focusVisibleMaxCheckTimes);
assert.strictEqual(wrapper.state().focusVisible, true);
});
});

describe('mounted tab press listener', () => {
let wrapper;
let instance;
Expand Down
11 changes: 10 additions & 1 deletion packages/material-ui/src/ButtonBase/focusVisible.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ const internal = {
keyUpEventTimeout: -1,
};

function findActiveElement(doc) {
let activeElement = doc.activeElement;
while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) {
activeElement = activeElement.shadowRoot.activeElement;
}
return activeElement;
}

export function detectFocusVisible(instance, element, callback, attempt = 1) {
warning(instance.focusVisibleCheckTime, 'Material-UI: missing instance.focusVisibleCheckTime.');
warning(
Expand All @@ -16,10 +24,11 @@ export function detectFocusVisible(instance, element, callback, attempt = 1) {

instance.focusVisibleTimeout = setTimeout(() => {
const doc = ownerDocument(element);
const activeElement = findActiveElement(doc);

if (
internal.focusKeyPressed &&
(doc.activeElement === element || element.contains(doc.activeElement))
(activeElement === element || element.contains(activeElement))
) {
callback();
} else if (attempt < instance.focusVisibleMaxCheckTimes) {
Expand Down

0 comments on commit d18ae71

Please sign in to comment.