From e6d22047dfc81b2cf660961f09ff857f4f9c7a53 Mon Sep 17 00:00:00 2001 From: Evert van der Weit Date: Fri, 16 Aug 2024 16:56:17 +0200 Subject: [PATCH 1/5] fix(a11y): Add role="list" on the list, fixes #17439 --- ui/src/components/item/QList.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ui/src/components/item/QList.js b/ui/src/components/item/QList.js index 22fccbd9418..dbe1df4e7ad 100644 --- a/ui/src/components/item/QList.js +++ b/ui/src/components/item/QList.js @@ -34,6 +34,12 @@ export default createComponent({ + (props.padding === true ? ' q-list--padding' : '') ) - return () => h(props.tag, { class: classes.value }, hSlot(slots.default)) + const role = computed(() => { + if (props.tag === 'ul') return undefined + if (props.tag === 'ol') return undefined + return 'list' + }) + + return () => h(props.tag, { class: classes.value, role: role.value }, hSlot(slots.default)) } }) From 3c8c74a393061fc6fcf33c6708aea4faefd2833f Mon Sep 17 00:00:00 2001 From: Evert van der Weit Date: Fri, 16 Aug 2024 16:57:33 +0200 Subject: [PATCH 2/5] test: add tests for QList component --- ui/src/components/item/QList.test.js | 171 +++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 ui/src/components/item/QList.test.js diff --git a/ui/src/components/item/QList.test.js b/ui/src/components/item/QList.test.js new file mode 100644 index 00000000000..029d2d7db2b --- /dev/null +++ b/ui/src/components/item/QList.test.js @@ -0,0 +1,171 @@ +import { mount, flushPromises } from '@vue/test-utils' +import { describe, test, expect } from 'vitest' + +import QList from './QList.js' + +describe('[QList API]', () => { + describe('[Props]', () => { + describe('[default attributes]', () => { + test('has a role="list" attribute with a div', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect(target.element.getAttribute('role')).toBeDefined() + expect(target.element.getAttribute('role')).toBe('list') + }) + + test('does not have a role="list" attribute with a ol', async () => { + const wrapper = mount(QList, { props: { tag: 'ol' } }) + + const target = wrapper.get('.q-list') + + expect(target.element.getAttribute('role')).toBe(null) + }) + + test('does not have a role="list" attribute with a ul', async () => { + const wrapper = mount(QList, { props: { tag: 'ul' } }) + + const target = wrapper.get('.q-list') + + expect(target.element.getAttribute('role')).toBe(null) + }) + }) + + describe('[(prop)bordered]', () => { + test('type Boolean has effect', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect(target.classes()).not.toContain('q-list--bordered') + + await wrapper.setProps({ bordered: true }) + await flushPromises() + + expect(target.classes()).toContain('q-list--bordered') + }) + }) + + describe('[(prop)dense]', () => { + test('type Boolean has effect', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect(target.classes()).not.toContain('q-list--dense') + + await wrapper.setProps({ dense: true }) + await flushPromises() + + expect(target.classes()).toContain('q-list--dense') + }) + }) + + describe('[(prop)separator]', () => { + test('type Boolean has effect', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect(target.classes()).not.toContain('q-list--separator') + + await wrapper.setProps({ separator: true }) + await flushPromises() + + expect(target.classes()).toContain('q-list--separator') + }) + }) + + describe('[(prop)dark]', () => { + test('type Boolean has effect', async () => { + const wrapper = mount(QList) + await wrapper.setProps({ dark: false }) + + const target = wrapper.get('.q-list') + + expect(target.classes()).not.toContain('q-list--dark') + + await wrapper.setProps({ dark: true }) + await flushPromises() + + expect(target.classes()).toContain('q-list--dark') + + await wrapper.setProps({ dark: false }) + await wrapper.vm.$q.dark.set(true) + await flushPromises() + + expect(target.classes()).not.toContain('q-list--dark') + }) + + test('type null has effect', async () => { + const wrapper = mount(QList) + await wrapper.vm.$q.dark.set(false) + + const target = wrapper.get('.q-list') + expect(target.classes()).not.toContain('q-list--dark') + + await wrapper.setProps({ dark: null }) + await flushPromises() + + expect(target.classes()).not.toContain('q-list--dark') + + await wrapper.vm.$q.dark.set(true) + + expect(target.classes()).toContain('q-list--dark') + }) + }) + + describe('[(prop)padding]', () => { + test('type Boolean has effect', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect(target.classes()).not.toContain('q-list--padding') + + await wrapper.setProps({ padding: true }) + await flushPromises() + + expect(target.classes()).toContain('q-list--padding') + }) + }) + + describe('[(prop)tag]', () => { + test('type String has effect', async () => { + const wrapper = mount(QList, { props: { tag: 'ol' } }) + + const target = wrapper.get('.q-list') + + expect( + target.element.tagName.toLowerCase() + ).toBe('ol') + }) + + test('default tag is div', async () => { + const wrapper = mount(QList) + + const target = wrapper.get('.q-list') + + expect( + target.element.tagName.toLowerCase() + ).toBe('div') + }) + }) + }) + + describe('[Slots]', () => { + describe('[(slot)default]', () => { + test('renders the content', () => { + const slotContent = 'some-slot-content' + const wrapper = mount(QList, { + slots: { + default: () => slotContent + } + }) + + expect(wrapper.html()).toContain(slotContent) + }) + }) + }) +}) From 4db36f6e856198dbbc563d3662842f989dee2e2e Mon Sep 17 00:00:00 2001 From: Evert van der Weit Date: Fri, 16 Aug 2024 16:58:20 +0200 Subject: [PATCH 3/5] docs: add watch mode instruction for a filter --- ui/testing/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/testing/README.md b/ui/testing/README.md index 8eb0b7ad997..ac17555f33b 100644 --- a/ui/testing/README.md +++ b/ui/testing/README.md @@ -43,6 +43,9 @@ $ pnpm test:specs --target # withOUT Vitest UI: $ pnpm test:watch +# to watch only a specific file pattern +$ pnpm test:watch "QList" + # with Vitest UI: $ pnpm test:watch:ui ``` From ce644bbf38775432a8905d435fc9c920142fa670 Mon Sep 17 00:00:00 2001 From: Evertvdw Date: Fri, 16 Aug 2024 18:31:51 +0200 Subject: [PATCH 4/5] Review: remove unnecessary defined check Co-authored-by: Yusuf Kandemir --- ui/src/components/item/QList.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/components/item/QList.test.js b/ui/src/components/item/QList.test.js index 029d2d7db2b..d27ada260fd 100644 --- a/ui/src/components/item/QList.test.js +++ b/ui/src/components/item/QList.test.js @@ -11,7 +11,6 @@ describe('[QList API]', () => { const target = wrapper.get('.q-list') - expect(target.element.getAttribute('role')).toBeDefined() expect(target.element.getAttribute('role')).toBe('list') }) From db8e1ca7f4d585b16ddc1bdf11a772d015038ef5 Mon Sep 17 00:00:00 2001 From: Evertvdw Date: Fri, 16 Aug 2024 18:32:59 +0200 Subject: [PATCH 5/5] Review: make it more concise Co-authored-by: Yusuf Kandemir --- ui/src/components/item/QList.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui/src/components/item/QList.js b/ui/src/components/item/QList.js index dbe1df4e7ad..b6010f71ee9 100644 --- a/ui/src/components/item/QList.js +++ b/ui/src/components/item/QList.js @@ -34,11 +34,7 @@ export default createComponent({ + (props.padding === true ? ' q-list--padding' : '') ) - const role = computed(() => { - if (props.tag === 'ul') return undefined - if (props.tag === 'ol') return undefined - return 'list' - }) + const role = computed(() => props.tag === 'ul' || props.tag === 'ol' ? undefined : 'list') return () => h(props.tag, { class: classes.value, role: role.value }, hSlot(slots.default)) }