mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-05-25 20:16:58 -05:00
feat(a11y): fix heading hierarchy across pages
- Home: greeting H2 → H1 (page needs a top-level heading) - Task detail: task ID H1 → span (only title should be H1) - Task detail: H6 breadcrumb → nav element - App header: project title H1 → span (avoids duplicate H1) Fixes WCAG 1.3.1 (Info and Relationships) and 2.4.6 (Headings).
This commit is contained in:
@@ -21,9 +21,9 @@
|
||||
v-if="currentProject?.id"
|
||||
class="project-title-wrapper"
|
||||
>
|
||||
<h1 class="project-title">
|
||||
<span class="project-title">
|
||||
{{ currentProject.title === '' ? $t('misc.loading') : getProjectTitle(currentProject) }}
|
||||
</h1>
|
||||
</span>
|
||||
|
||||
<BaseButton
|
||||
v-if="!isEditorContentEmpty(currentProject.description)"
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
:color="getHexColor(task.hexColor)"
|
||||
/>
|
||||
<BaseButton @click="copyUrl">
|
||||
<h1 class="title task-id">
|
||||
<span class="title task-id">
|
||||
{{ textIdentifier }}
|
||||
</h1>
|
||||
</span>
|
||||
</BaseButton>
|
||||
</div>
|
||||
<Done
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="content has-text-centered">
|
||||
<h2 v-if="salutation">
|
||||
<h1 v-if="salutation">
|
||||
{{ salutation }}
|
||||
</h2>
|
||||
</h1>
|
||||
|
||||
<Message
|
||||
v-if="deletionScheduledAt !== null"
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
@update:task="Object.assign(task, $event)"
|
||||
@close="$emit('close')"
|
||||
/>
|
||||
<h6
|
||||
<nav
|
||||
v-if="project?.id"
|
||||
aria-label="Breadcrumb"
|
||||
class="subtitle"
|
||||
>
|
||||
<template
|
||||
@@ -60,7 +61,7 @@
|
||||
:can-write="canWrite"
|
||||
@update:task="Object.assign(task, $event)"
|
||||
/>
|
||||
</h6>
|
||||
</nav>
|
||||
|
||||
<ChecklistSummary :task="task" />
|
||||
|
||||
|
||||
@@ -278,8 +278,8 @@ test.describe('Task', () => {
|
||||
await page.goto(`/tasks/${tasks[0].id}`)
|
||||
|
||||
await expect(page.locator('.task-view h1.title.input')).toContainText(tasks[0].title)
|
||||
await expect(page.locator('.task-view h1.title.task-id')).toContainText('#1')
|
||||
await expect(page.locator('.task-view h6.subtitle')).toContainText(projects[0].title)
|
||||
await expect(page.locator('.task-view span.title.task-id')).toContainText('#1')
|
||||
await expect(page.locator('.task-view nav.subtitle')).toContainText(projects[0].title)
|
||||
await expect(page.locator('.task-view .details.content.description')).toContainText(tasks[0].description)
|
||||
await expect(page.locator('.task-view .action-buttons p.created')).toContainText('Created')
|
||||
})
|
||||
@@ -328,7 +328,7 @@ test.describe('Task', () => {
|
||||
|
||||
await page.goto(`/tasks/${tasks[0].id}`)
|
||||
|
||||
await expect(page.locator('.task-view h1.title.task-id')).toContainText(`${projects[0].identifier}-${tasks[0].index}`)
|
||||
await expect(page.locator('.task-view span.title.task-id')).toContainText(`${projects[0].identifier}-${tasks[0].index}`)
|
||||
})
|
||||
|
||||
test('Can edit the description', async ({authenticatedPage: page}) => {
|
||||
@@ -367,7 +367,7 @@ test.describe('Task', () => {
|
||||
await page.locator('.task-view .details.content.description .tiptap button.done-edit', {timeout: 30_000}).click()
|
||||
await page.locator('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror').fill('New Description')
|
||||
|
||||
await page.locator('.task-view h6.subtitle a').first().click()
|
||||
await page.locator('.task-view nav.subtitle a').first().click()
|
||||
|
||||
await page.goto('/tasks/1')
|
||||
await expect(page.locator('.task-view .details.content.description')).toContainText('New Description')
|
||||
@@ -443,7 +443,7 @@ test.describe('Task', () => {
|
||||
await expect(page.locator('.task-view .content.details .field .multiselect.control .search-results')).toBeVisible({timeout: 5000})
|
||||
await page.locator('.task-view .content.details .field .multiselect.control .search-results').locator('> *').first().click()
|
||||
|
||||
await expect(page.locator('.task-view h6.subtitle')).toContainText(projects[1].title)
|
||||
await expect(page.locator('.task-view nav.subtitle')).toContainText(projects[1].title)
|
||||
await expect(page.locator('.global-notification')).toContainText('Success')
|
||||
})
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ test.describe('Login', () => {
|
||||
test('Should log in with the right credentials', async ({page}) => {
|
||||
await page.goto('/login')
|
||||
await login(page)
|
||||
await expect(page.locator('main h2')).toContainText(credentials.username)
|
||||
await expect(page.locator('main h1')).toContainText(credentials.username)
|
||||
})
|
||||
|
||||
test('Should fail with a bad password', async ({page}) => {
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('OpenID Login', () => {
|
||||
|
||||
// Should redirect back to the app
|
||||
await expect(page).toHaveURL(/\//)
|
||||
await expect(page.locator('main.app-content .content h2')).toContainText('test')
|
||||
await expect(page.locator('main.app-content .content h1')).toContainText('test')
|
||||
await expect(page.locator('.show-tasks h3')).toContainText('Current Tasks')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe('Registration', () => {
|
||||
await page.locator('#password').fill(fixture.password)
|
||||
await page.locator('#register-submit').click()
|
||||
await expect(page).toHaveURL('/')
|
||||
await expect(page.locator('main h2')).toContainText(fixture.username)
|
||||
await expect(page.locator('main h1')).toContainText(fixture.username)
|
||||
})
|
||||
|
||||
test('Should fail', async ({page, apiContext}) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ async function loginViaBrowser(page, username: string) {
|
||||
await page.locator('input[id=password]').fill(TEST_PASSWORD)
|
||||
await page.locator('.button').filter({hasText: 'Login'}).click()
|
||||
await expect(page).toHaveURL('/')
|
||||
await expect(page.locator('main h2')).toContainText(username)
|
||||
await expect(page.locator('main h1')).toContainText(username)
|
||||
|
||||
// Wait for the proactive refresh (from useRenewTokenOnFocus) to complete
|
||||
// so it doesn't race with our test assertions.
|
||||
|
||||
Reference in New Issue
Block a user