Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions resource/js/tab-alpha.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,16 +230,54 @@ function startAlphaApp () {
'chooseLetterMessage',
'ariaLiveMessage'
],
data () {
return {
conceptInFocus: 0
}
},
emits: ['loadConcepts', 'selectConcept'],
inject: ['partialPageLoad', 'getConceptURL', 'showNotation'],
methods: {
loadConcepts (event, letter) {
event.preventDefault()
this.conceptInFocus = 0
this.$emit('loadConcepts', letter)
},
loadConcept (event, uri) {
loadConcept (event, uri, i) {
this.conceptInFocus = i
partialPageLoad(event, getConceptURL(uri))
this.$emit('selectConcept', uri)
},
handleKeydownEvent (e) {
if (e.key === ' ') {
// Click on link currently in focus
e.preventDefault()
this.$refs['concept' + this.conceptInFocus][0].click()
} else if (e.key === 'ArrowDown') {
// On last element move focus to first list item, otherwise next list item
e.preventDefault()
this.conceptInFocus = (this.conceptInFocus + 1) % this.indexConcepts.length
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'ArrowUp') {
// On first element move focus to last list item, otherwise to previous list item
e.preventDefault()
if (this.conceptInFocus === 0) {
this.conceptInFocus = this.indexConcepts.length - 1
} else {
this.conceptInFocus -= 1
}
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'End') {
// Move focus to last list item
e.preventDefault()
this.conceptInFocus = this.indexConcepts.length - 1
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'Home') {
// Move focus to first list item
e.preventDefault()
this.conceptInFocus = 0
this.$refs['concept' + this.conceptInFocus][0].focus()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Function with high complexity (count = 7): handleKeydownEvent [qlty:function-complexity]

}
},
template: `
Expand Down Expand Up @@ -269,21 +307,25 @@ function startAlphaApp () {
</fieldset>
</template>

<div class="sidebar-list" :style="listStyle" ref="list">
<div class="sidebar-list" tabindex="-1" :style="listStyle" ref="list">
<template v-if="loadingConcepts || loadingLetters">
<div>
{{ this.loadingMessage }} <i class="fa-solid fa-spinner fa-spin-pulse"></i>
</div>
</template>
<template v-else>
<ul class="list-group" v-if="indexConcepts.length !== 0">
<li v-for="concept in indexConcepts" class="list-group-item py-1 px-2">
<li v-for="(concept, i) in indexConcepts" class="list-group-item py-1 px-2">
<template v-if="concept.altLabel">
<span class="fst-italic">{{ concept.altLabel }}</span>
<i class="fa-solid fa-arrow-right"></i>
</template>
<a :class="{ 'selected': selectedConcept === concept.uri }"
:href="getConceptURL(concept.uri)" @click="loadConcept($event, concept.uri)"
:href="getConceptURL(concept.uri)"
:tabindex="i === conceptInFocus ? 0 : -1"
:ref="'concept' + i"
@click="loadConcept($event, concept.uri, i)"
@keydown="handleKeydownEvent($event)"
>
{{ concept.prefLabel }}{{ showNotation && concept.qualifier ? ' (' + concept.qualifier + ')' : '' }}
<span class="visually-hidden">{{ toConceptPageAriaMessage }}</span>
Expand Down
76 changes: 70 additions & 6 deletions resource/js/tab-changes.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,40 +191,101 @@ function startChangesApp () {

tabChangesApp.component('tab-changes', {
props: ['changedConcepts', 'selectedConcept', 'loadingConcepts', 'loadingMoreConcepts', 'loadingMessage', 'toConceptPageAriaMessage', 'listStyle'],
data () {
return {
conceptInFocus: 0,
changedConceptsLength: 0
}
},
inject: ['partialPageLoad', 'getConceptURL'],
emits: ['selectConcept'],
methods: {
loadConcept (event, uri) {
loadConcept (event, uri, i) {
this.conceptInFocus = i
partialPageLoad(event, getConceptURL(uri))
this.$emit('selectConcept', uri)
},
getIndexedConcepts () {
// Give each uri and replacedBy in changedConcepts a unique index for keyboard navigation
let counter = 0

const indexed = new Map(
[...this.changedConcepts].map(([month, entries]) => [
month,
entries.map(entry => {
const result = { ...entry, index: counter++ }
if (entry.replacedBy) result.replacedByIndex = counter++
return result
})
])
)
this.changedConceptsLength = counter
return indexed
},
handleKeydownEvent (e) {
if (e.key === ' ') {
// Click on link currently in focus
e.preventDefault()
this.$refs['concept' + this.conceptInFocus][0].click()
} else if (e.key === 'ArrowDown') {
// On last element move focus to first list item, otherwise next list item
e.preventDefault()
this.conceptInFocus = (this.conceptInFocus + 1) % this.changedConceptsLength
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'ArrowUp') {
// On first element move focus to last list item, otherwise to previous list item
e.preventDefault()
if (this.conceptInFocus === 0) {
this.conceptInFocus = this.changedConceptsLength - 1
} else {
this.conceptInFocus -= 1
}
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'End') {
// Move focus to last list item
e.preventDefault()
this.conceptInFocus = this.changedConceptsLength - 1
this.$refs['concept' + this.conceptInFocus][0].focus()
} else if (e.key === 'Home') {
// Move focus to first list item
e.preventDefault()
this.conceptInFocus = 0
this.$refs['concept' + this.conceptInFocus][0].focus()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Function with high complexity (count = 7): handleKeydownEvent [qlty:function-complexity]

}
Comment on lines +225 to 255
},
template: `
<div class="sidebar-list pt-3" :style="listStyle" ref="list">
<div class="sidebar-list pt-3" tabindex="-1" :style="listStyle" ref="list">
<template v-if="loadingConcepts">
<div>
{{ loadingMessage }} <i class="fa-solid fa-spinner fa-spin-pulse"></i>
</div>
</template>
<template v-else>
<ul class="list-group" v-if="changedConcepts.length !== 0">
<template v-for="[month, concepts] in changedConcepts">
<template v-for="[month, concepts] in getIndexedConcepts()">
<li class="list-group-item py-1 px-2">
<h2 class="pb-1">{{ month }}</h2>
</li>
<li v-for="concept in concepts" class="list-group-item py-1 px-2">
<template v-if="concept.replacedBy">
<a :class="{ 'selected': selectedConcept === concept.uri }"
:href="getConceptURL(concept.uri)"
@click="loadConcept($event, concept.uri)"
:tabindex="concept.index === conceptInFocus ? 0 : -1"
:ref="'concept' + concept.index"
@click="loadConcept($event, concept.uri, concept.index)"
@keydown="handleKeydownEvent($event)"
>
<s>{{ concept.prefLabel }}</s>
<span class="visually-hidden">{{ toConceptPageAriaMessage }}</span>
</a>
<i class="fa-solid fa-arrow-right"></i>
<a :class="{ 'selected': selectedConcept === concept.replacedBy }"
:href="getConceptURL(concept.replacedBy)"
@click="loadConcept($event, concept.replacedBy)"
:tabindex="concept.replacedByIndex === conceptInFocus ? 0 : -1"
:ref="'concept' + concept.replacedByIndex"
@click="loadConcept($event, concept.replacedBy, concept.replacedByIndex)"
@keydown="handleKeydownEvent($event)"
>
{{ concept.replacingLabel }}
<span class="visually-hidden">{{ toConceptPageAriaMessage }}</span>
Expand All @@ -233,7 +294,10 @@ function startChangesApp () {
<template v-else>
<a :class="{ 'selected': selectedConcept === concept.uri }"
:href="getConceptURL(concept.uri)"
@click="loadConcept($event, concept.uri)"
:tabindex="concept.index === conceptInFocus ? 0 : -1"
:ref="'concept' + concept.index"
@click="loadConcept($event, concept.uri, concept.index)"
@keydown="handleKeydownEvent($event)"
>
{{ concept.prefLabel }}
<span class="visually-hidden">{{ toConceptPageAriaMessage }}</span>
Expand Down
16 changes: 16 additions & 0 deletions tests/cypress/template/sidebar-alpha.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,21 @@ describe('Alphabetical index', () => {
cy.get('.aria-live-message').invoke('text').should('equal', 'Concepts loaded for letter B')
// Check that new concepts are loaded
cy.get('#tab-alphabetical').find('.sidebar-list li').first().invoke('text').should('contain', 'birch bark manuscripts')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test only verifies moving between entries, but not opening them (with space and/or enter). I think that's pretty crucial functionality so it should be tested as well.

// Press tab
cy.press(Cypress.Keyboard.Keys.TAB)
// Check that first list item has focus
cy.get('#tab-alphabetical').find('.sidebar-list li a').eq(0).should('have.focus')
// Press down arrow key
cy.press(Cypress.Keyboard.Keys.DOWN)
// Check that second list item has focus
cy.get('#tab-alphabetical').find('.sidebar-list li a').eq(1).should('have.focus')
// Press up arrow key
cy.press(Cypress.Keyboard.Keys.UP)
// Check that first list item has focus again
cy.get('#tab-alphabetical').find('.sidebar-list li a').eq(0).should('have.focus')
// Check that pressing space opens concept page
cy.press(Cypress.Keyboard.Keys.SPACE)
cy.get('#concept-heading h1', {'timeout': 15000}).invoke('text').should('equal', 'birch bark manuscripts')
})
})
24 changes: 23 additions & 1 deletion tests/cypress/template/sidebar-changes.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,27 @@ describe('New and removed view', () => {
// Check that concepts have correct Aria labels
cy.get('#tab-changes').find('.sidebar-list li a span').eq(0).invoke('text').should('contain', 'Gå till begreppssidan')

})
})
it('Keyboard navigation', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ditto, test opening (with space/enter) as well.

// go to YSO vocab front page in English
cy.visit('/yso/en/')
// Click on changes tab and wait until list has loaded
cy.get('#changes').click()
cy.get('#tab-changes').find('.sidebar-list li a span').eq(0)
// Press tab key
cy.press(Cypress.Keyboard.Keys.TAB)
// Check that first list item has focus
cy.get('#tab-changes').find('.sidebar-list a').eq(0).should('have.focus')
// Press down arrow key
cy.press(Cypress.Keyboard.Keys.DOWN)
// Check that second list item has focus
cy.get('#tab-changes').find('.sidebar-list a').eq(1).should('have.focus')
// Press up arrow key
cy.press(Cypress.Keyboard.Keys.UP)
// Check that first list item has focus again
cy.get('#tab-changes').find('.sidebar-list a').eq(0).should('have.focus')
// Check that pressing space opens concept page
cy.press(Cypress.Keyboard.Keys.SPACE)
cy.get('#concept-heading h1', {'timeout': 15000}).invoke('text').should('equal', 'Bell beaker culture')
})
})
Loading