-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce more customizable skip-nav (#208) (GAUD-5025)
- Loading branch information
Showing
15 changed files
with
273 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
"browsers": [ | ||
{ | ||
"name": "Chromium", | ||
"version": 120 | ||
"version": 121 | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import './d2l-navigation-skip.js'; | ||
import { html, LitElement } from 'lit'; | ||
import { FocusMixin } from '@brightspace-ui/core/mixins/focus/focus-mixin.js'; | ||
import { LocalizeNavigationElement } from './components/localize-navigation-element.js'; | ||
import { querySelectorComposed } from '@brightspace-ui/core/helpers/dom.js'; | ||
|
||
class NavigationSkipMain extends FocusMixin(LocalizeNavigationElement(LitElement)) { | ||
|
||
static get focusElementSelector() { | ||
return 'd2l-navigation-skip'; | ||
} | ||
|
||
render() { | ||
return html`<d2l-navigation-skip text="${this.localize('skipNav')}" @click="${this._handleSkipNav}" class="vdiff-target"></d2l-navigation-skip>`; | ||
} | ||
|
||
_handleSkipNav() { | ||
const elem = querySelectorComposed(document, 'main') || | ||
querySelectorComposed(document, '[role="main"]') || | ||
querySelectorComposed(document, 'h1'); | ||
if (elem) { | ||
elem.tabIndex = -1; | ||
elem.focus(); | ||
} else { | ||
this.dispatchEvent(new CustomEvent('d2l-navigation-skip-fail', { bubbles: false, composed: false })); | ||
} | ||
} | ||
|
||
} | ||
|
||
customElements.define('d2l-navigation-skip-main', NavigationSkipMain); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<link rel="stylesheet" href="/node_modules/@brightspace-ui/core/components/demo/styles.css" type="text/css"> | ||
<script type="module"> | ||
import '@brightspace-ui/core/components/demo/demo-page.js'; | ||
import '../d2l-navigation-skip.js'; | ||
import '../d2l-navigation-skip-main.js'; | ||
</script> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<meta charset="UTF-8"> | ||
</head> | ||
<body> | ||
<d2l-demo-page page-title="d2l-navigation-skip"> | ||
<h2>Custom</h2> | ||
<d2l-demo-snippet> | ||
<d2l-navigation-skip text="Skip to custom place" id="custom"></d2l-navigation-skip> | ||
<button id="custom-target">Skip to here</button> | ||
<script> | ||
document.querySelector('#custom').addEventListener('click', () => { | ||
document.querySelector('#custom-target').focus(); | ||
}); | ||
</script> | ||
</d2l-demo-snippet> | ||
|
||
<h2>Main</h2> | ||
<d2l-demo-snippet> | ||
<d2l-navigation-skip-main></d2l-navigation-skip-main> | ||
</d2l-demo-snippet> | ||
</d2l-demo-page> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import '../d2l-navigation-skip-main.js'; | ||
import { clickElem, expect, fixture, focusElem, html, oneEvent, sendKeysElem } from '@brightspace-ui/testing'; | ||
import { getComposedActiveElement } from '@brightspace-ui/core/helpers/focus.js'; | ||
|
||
const mainFixture = html`<d2l-navigation-skip-main></d2l-navigation-skip-main>`; | ||
|
||
function getAnchor(elem) { | ||
return elem.shadowRoot.querySelector('d2l-navigation-skip').shadowRoot.querySelector('a'); | ||
} | ||
|
||
describe('d2l-navigation-skip-main', () => { | ||
|
||
describe('events', () => { | ||
|
||
let elem, anchor; | ||
beforeEach(async() => { | ||
elem = await fixture(mainFixture); | ||
anchor = getAnchor(elem); | ||
}); | ||
|
||
it('should fire click event when clicked with mouse', async() => { | ||
const p = oneEvent(elem, 'click'); | ||
await focusElem(anchor); | ||
clickElem(anchor); | ||
await p; | ||
}); | ||
|
||
it('should fire click event when ENTER is pressed', async() => { | ||
const p = oneEvent(elem, 'click'); | ||
sendKeysElem(anchor, 'press', 'Enter'); | ||
await p; | ||
}); | ||
|
||
it('should delegate focus to anchor', async() => { | ||
await focusElem(elem); | ||
expect(getComposedActiveElement()).to.equal(anchor); | ||
}); | ||
|
||
}); | ||
|
||
describe('skip logic', () => { | ||
|
||
it('should focus on main element if present', async() => { | ||
const elem = await fixture(html` | ||
<div> | ||
${mainFixture} | ||
<main>main1</main> | ||
<div role="main">main2</div> | ||
<h1>heading</h1> | ||
</div> | ||
`); | ||
const anchor = getAnchor(elem.querySelector('d2l-navigation-skip-main')); | ||
await sendKeysElem(anchor, 'press', 'Enter'); | ||
expect(getComposedActiveElement()).to.equal(elem.querySelector('main')); | ||
}); | ||
|
||
it('should focus on role="main" element if no main', async() => { | ||
const elem = await fixture(html` | ||
<div> | ||
${mainFixture} | ||
<div role="main">main2</div> | ||
<h1>heading</h1> | ||
</div> | ||
`); | ||
const anchor = getAnchor(elem.querySelector('d2l-navigation-skip-main')); | ||
await sendKeysElem(anchor, 'press', 'Enter'); | ||
expect(getComposedActiveElement()).to.equal(elem.querySelector('[role="main"]')); | ||
}); | ||
|
||
it('should focus on h1 element if no main or role="main"', async() => { | ||
const elem = await fixture(html` | ||
<div> | ||
${mainFixture} | ||
<h1>heading</h1> | ||
</div> | ||
`); | ||
const anchor = getAnchor(elem.querySelector('d2l-navigation-skip-main')); | ||
await sendKeysElem(anchor, 'press', 'Enter'); | ||
expect(getComposedActiveElement()).to.equal(elem.querySelector('h1')); | ||
}); | ||
|
||
it('should dispatch "d2l-navigation-skip-fail" event if no focus targets are found', async() => { | ||
const elem = await fixture(mainFixture); | ||
const anchor = getAnchor(elem); | ||
sendKeysElem(anchor, 'press', 'Enter'); | ||
await oneEvent(elem, 'd2l-navigation-skip-fail'); | ||
}); | ||
|
||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import '../../d2l-navigation-skip-main.js'; | ||
import { expect, fixture, focusElem, html } from '@brightspace-ui/testing'; | ||
|
||
const mainFixture = html` | ||
<div class="width: 600px;"> | ||
<d2l-navigation-skip-main class="vdiff-include"></d2l-navigation-skip-main> | ||
<main> | ||
<h1>Heading</h1> | ||
<p>Some content</p> | ||
</main> | ||
</div> | ||
`; | ||
|
||
describe('d2l-navigation-skip-main', () => { | ||
|
||
['en', 'ar'].forEach(lang => { | ||
it(lang, async() => { | ||
const elem = await fixture(mainFixture, { lang }); | ||
await focusElem(elem.querySelector('d2l-navigation-skip-main')); | ||
await expect(elem).to.be.golden(); | ||
}); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import '../d2l-navigation-skip.js'; | ||
import { clickElem, expect, fixture, focusElem, html, oneEvent, sendKeysElem } from '@brightspace-ui/testing'; | ||
import { createMessage } from '@brightspace-ui/core/mixins/property-required/property-required-mixin.js'; | ||
import { getComposedActiveElement } from '@brightspace-ui/core/helpers/focus.js'; | ||
|
||
const customFixture = html`<d2l-navigation-skip text="Skip to custom place"></d2l-navigation-skip>`; | ||
|
||
describe('d2l-navigation-skip', () => { | ||
|
||
it('should throw if text is not provided', async() => { | ||
const elem = await fixture(html`<d2l-navigation-skip><d2l-navigation-skip>`); | ||
expect(() => elem.flushRequiredPropertyErrors()) | ||
.to.throw(TypeError, createMessage(elem, 'text')); | ||
}); | ||
|
||
describe('accessibility', () => { | ||
|
||
it('should pass all aXe tests', async() => { | ||
const elem = await fixture(customFixture); | ||
await focusElem(elem); | ||
await expect(elem).to.be.accessible(); | ||
}); | ||
|
||
}); | ||
|
||
describe('events', () => { | ||
|
||
let elem, anchor; | ||
beforeEach(async() => { | ||
elem = await fixture(customFixture); | ||
anchor = elem.shadowRoot.querySelector('a'); | ||
}); | ||
|
||
it('should fire click event when clicked with mouse', async() => { | ||
const p = oneEvent(elem, 'click'); | ||
await focusElem(anchor); | ||
clickElem(anchor); | ||
await p; | ||
}); | ||
|
||
it('should fire click event when ENTER is pressed', async() => { | ||
const p = oneEvent(elem, 'click'); | ||
sendKeysElem(anchor, 'press', 'Enter'); | ||
await p; | ||
}); | ||
|
||
it('should delegate focus to anchor', async() => { | ||
await focusElem(elem); | ||
expect(getComposedActiveElement()).to.equal(anchor); | ||
}); | ||
|
||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import '../../d2l-navigation-skip.js'; | ||
import { expect, fixture, focusElem, html } from '@brightspace-ui/testing'; | ||
|
||
describe('d2l-navigation-skip', () => { | ||
|
||
it('focus', async() => { | ||
const elem = await fixture(html` | ||
<div class="width: 600px;"> | ||
<d2l-navigation-skip text="Skip to custom place" class="vdiff-include"></d2l-navigation-skip> | ||
<p>Some content</p> | ||
</div> | ||
`); | ||
await focusElem(elem.querySelector('d2l-navigation-skip')); | ||
await expect(elem).to.be.golden(); | ||
}); | ||
|
||
}); |