Skip to content

Commit f33d34b

Browse files
authored
Merge pull request #11 from shwilliam/multiple-destinations
Support multiple destinations
2 parents 337451d + efd8fd9 commit f33d34b

File tree

9 files changed

+270
-4
lines changed

9 files changed

+270
-4
lines changed

cypress.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"integrationFolder": "tests/e2e/integration",
66
"fileServerFolder": "demo",
77
"pluginsFile": false,
8-
"supportFile": false
8+
"supportFile": "cypress/support"
99
}

cypress/support/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('cypress-plugin-tab')

demo/skip-to-list.html

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
7+
<title>Using vue-skip-to</title>
8+
9+
<style>
10+
main:focus, footer:focus {
11+
background-color: yellow;
12+
}
13+
</style>
14+
15+
<script src="https://unpkg.com/vue"></script>
16+
<script src="vue-skip-to.js"></script>
17+
</head>
18+
<body>
19+
20+
<div id="app">
21+
<!-- data-vst used for internal testing, it is NOT required -->
22+
<vue-skip-to-list
23+
:to="[
24+
{anchor: '#main', label: 'Main'},
25+
{anchor: '#footer', label: 'Footer', ariaLabel: 'Skip to about'},
26+
]"
27+
data-vst="skip-to-list"
28+
>
29+
Skip links
30+
</vue-skip-to-list>
31+
32+
<header>
33+
<h1>Press tab</h1>
34+
</header>
35+
36+
<main id="main">
37+
<p>
38+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent ornare elit vitae felis tincidunt commodo. Phasellus volutpat pharetra consectetur. Mauris eu orci fermentum, maximus odio ut, consequat dui. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec et tortor sagittis, euismod risus vel, pulvinar urna. Suspendisse non facilisis leo. Quisque rhoncus vel mauris sed dapibus. Nunc nibh mauris, fringilla ut nisi at, maximus varius magna. Proin hendrerit a orci quis ullamcorper. Curabitur molestie eros quis eros tempus auctor. Proin luctus nibh quis sem dictum tempus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed eleifend, tortor tristique efficitur egestas, nulla libero tincidunt quam, vel pulvinar velit nulla id libero. Maecenas nisl quam, dictum in tempus in, ultrices a felis.
39+
</p>
40+
<p>
41+
Aliquam mattis convallis est et cursus. Phasellus tincidunt efficitur dui, non fringilla ipsum dignissim nec. Nam consectetur ante vitae malesuada rutrum. Praesent nec varius magna. Sed sem nisi, tempor eu venenatis vitae, consectetur eu sapien. Suspendisse sit amet massa lacinia purus ultrices lacinia. Aliquam non cursus quam, ac elementum enim. Morbi dignissim lacus nulla, sit amet interdum diam eleifend in. In hac habitasse platea dictumst. Suspendisse eget erat eu eros placerat pharetra vel eget metus. Morbi porta ex sed erat vestibulum, ac varius ex euismod. Nunc iaculis ornare lacus a egestas. Cras cursus facilisis mi, nec vulputate leo. Fusce varius varius arcu sit amet mollis.
42+
</p>
43+
</main>
44+
45+
<footer id="footer">
46+
<p>
47+
Sed elit nunc, volutpat in urna vel, hendrerit vulputate justo. Etiam pulvinar id ligula in ultrices. Suspendisse nulla risus, accumsan quis sagittis ac, faucibus eget dolor. Curabitur ullamcorper magna eget consequat facilisis. Cras massa leo, tristique nec tempus ac, auctor in arcu. Quisque a faucibus ex, congue eleifend ante. Praesent ultrices arcu neque, eget lacinia erat ultrices eget.
48+
</p>
49+
</footer>
50+
</div>
51+
<script>
52+
var App = new Vue({
53+
el: '#app'
54+
})
55+
</script>
56+
</body>
57+
</html>

package-lock.json

Lines changed: 70 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"babel-jest": "^26.0.1",
6767
"chokidar": "^3.4.0",
6868
"cypress": "^4.5.0",
69+
"cypress-plugin-tab": "^1.0.5",
6970
"eslint": "^7.0.0",
7071
"eslint-plugin-cypress": "^2.10.3",
7172
"eslint-plugin-import": "^2.20.2",

src/VueSkipTo.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
class="vue-skip-to"
44
:href="to"
55
@click.prevent="handleFocusElement"
6+
@focus="$emit('focus')"
7+
@blur="$emit('blur')"
68
>
79
<slot>{{ text }}</slot>
810
</a>

src/VueSkipToList.vue

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<template>
2+
<div :class="containerClassNames">
3+
<label>
4+
5+
<p class="vue-skip-to__label">
6+
<slot>Skip to</slot>
7+
</p>
8+
9+
<nav class="vue-skip-to__nav">
10+
<ul class="vue-skip-to__nav-list">
11+
<li
12+
v-for="el in to"
13+
:key="el.anchor"
14+
class="vue-skip-to__nav-list-item"
15+
>
16+
<vue-skip-to
17+
:to="el.anchor"
18+
:ariaLabel="el.ariaLabel"
19+
@focus="labelVisible = true"
20+
@blur="labelVisible = false"
21+
class="vue-skip-to vue-skip-to--relative"
22+
>
23+
{{ el.label }}
24+
</vue-skip-to>
25+
</li>
26+
</ul>
27+
</nav>
28+
29+
</label>
30+
</div>
31+
</template>
32+
33+
<script>
34+
export default {
35+
name: 'VueSkipToList',
36+
37+
props: {
38+
// TODO: allow modifying `<skip-to>` props
39+
to: {
40+
validator: function (val) {
41+
return Array.isArray(val) &&
42+
val.every(({ anchor, label }) => (
43+
typeof anchor === 'string' &&
44+
anchor.startsWith('#') &&
45+
typeof String(label) === 'string'
46+
))
47+
}
48+
}
49+
},
50+
51+
data () {
52+
return {
53+
labelVisible: false
54+
}
55+
},
56+
57+
computed: {
58+
containerClassNames: function () {
59+
return {
60+
'vue-skip-to__list-container': true,
61+
'vue-skip-to__list-container--focus': this.labelVisible
62+
}
63+
}
64+
}
65+
66+
}
67+
</script>
68+
69+
<style scoped>
70+
.vue-skip-to__list-container {
71+
position: absolute;
72+
left: -10000px;
73+
top: 0;
74+
border: 1px solid #000;
75+
}
76+
77+
.vue-skip-to__list-container--focus {
78+
background-color: #fff;
79+
left: 0;
80+
}
81+
82+
.vue-skip-to__label {
83+
font-weight: 800;
84+
margin: 8px 0 0 0;
85+
padding: 8px 24px 8px 10px;
86+
border-bottom: 1px solid #000;
87+
}
88+
89+
.vue-skip-to__nav-list {
90+
display: flex;
91+
flex-direction: column;
92+
padding: 0;
93+
margin: 0 0 10px 0;
94+
list-style-type: none;
95+
}
96+
97+
.vue-skip-to__nav-list-item {
98+
position: relative;
99+
}
100+
101+
/* override `<vue-skip-to>` styles */
102+
.vue-skip-to.vue-skip-to--relative {
103+
position: relative;
104+
left: unset;
105+
top: unset;
106+
display: block;
107+
padding: 8px 24px 8px 10px;
108+
color: #000;
109+
text-decoration: none;
110+
}
111+
112+
.vue-skip-to.vue-skip-to--relative:focus {
113+
color: #fff;
114+
}
115+
</style>

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import VueSkipTo from './VueSkipTo.vue'
2+
import VueSkipToList from './VueSkipToList.vue'
23

34
export default function install (Vue) {
45
if (install.installed) return
56
install.installed = true
67
Vue.component('VueSkipTo', VueSkipTo)
8+
Vue.component('VueSkipToList', VueSkipToList)
79
}
810

911
// auto install

tests/e2e/integration/skip-to-list.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
describe('Skip link list', () => {
2+
beforeEach(() => {
3+
cy.visit('/skip-to-list.html')
4+
})
5+
6+
it('Should render list label', () => {
7+
cy
8+
.get('[data-vst="skip-to-list"]')
9+
.should('contain', 'Skip links')
10+
})
11+
12+
it('Should traverse links on tab', () => {
13+
cy.get('body').tab().tab()
14+
cy.focused().should('contain', 'Footer')
15+
})
16+
17+
it('Should focus relevant element on link press', () => {
18+
cy.get('body').tab().click()
19+
cy.focused().should('have.id', 'main')
20+
})
21+
})

0 commit comments

Comments
 (0)