fix(overlays): Allow elements with display:contents to be focusable in keyboard event trapping (#1709)

* fix: isVisible check on elements with display:contents

* Create eight-years-pump.md

* chore: adds additional test
This commit is contained in:
Stefan Pearson 2022-05-25 15:40:45 +01:00 committed by GitHub
parent ae53377ce4
commit 915de370d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
"@lion/overlays": patch
---
fix: isVisible check on elements with display:contents

View file

@ -4,6 +4,11 @@
const hasStyleVisibility = ({ visibility, display }) => const hasStyleVisibility = ({ visibility, display }) =>
visibility !== 'hidden' && display !== 'none'; visibility !== 'hidden' && display !== 'none';
/**
* @param {CSSStyleDeclaration} styles
*/
const isDisplayContents = ({ display }) => display === 'contents';
/** /**
* @param {HTMLElement} element * @param {HTMLElement} element
* @returns {boolean} Whether the element is visible * @returns {boolean} Whether the element is visible
@ -24,12 +29,20 @@ export function isVisible(element) {
return false; return false;
} }
const computedStyle = window.getComputedStyle(element);
// Check computed styles // Check computed styles
// matches display: none, visbility: hidden on element and visibility: hidden from parent // matches display: none, visibility: hidden on element and visibility: hidden from parent
if (!hasStyleVisibility(window.getComputedStyle(element))) { if (!hasStyleVisibility(computedStyle)) {
return false; return false;
} }
// Allow element that delegates layout (i.e. display: contents)
// matches display: contents
if (isDisplayContents(computedStyle)) {
return true;
}
// display: none is not inherited, so finally check if element has calculated width or height // display: none is not inherited, so finally check if element has calculated width or height
// matches display: none from parent // matches display: none from parent
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length); return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);

View file

@ -53,12 +53,16 @@ describe('getFocusableElements()', () => {
<input id='el4'> <input id='el4'>
</div> </div>
</div> </div>
<div style='display: contents;'>
<button id='el5'></button>
</div>
</div> </div>
`); `);
const nodes = getFocusableElements(element); const nodes = getFocusableElements(element);
const ids = nodes.map(n => n.id); const ids = nodes.map(n => n.id);
expect(ids).eql(['el1', 'el2', 'el3', 'el4']); expect(ids).eql(['el1', 'el2', 'el3', 'el4', 'el5']);
}); });
it('skips elements that should not receive focus', async () => { it('skips elements that should not receive focus', async () => {

View file

@ -142,4 +142,16 @@ describe('isVisible()', () => {
const target = /** @type {HTMLElement} */ (element.querySelector('#target')); const target = /** @type {HTMLElement} */ (element.querySelector('#target'));
expect(isVisible(target)).to.equal(false); expect(isVisible(target)).to.equal(false);
}); });
it('returns true when display contents', async () => {
const element = /** @type {HTMLElement} */ (
await fixture(`
<div style="display: contents;">
<div style="width:10px; height:10px;"></div>
</div>
`)
);
expect(isVisible(element)).to.equal(true);
});
}); });