Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Listbox): horizontal orientation not working #1299

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
89 changes: 89 additions & 0 deletions packages/radix-vue/src/Listbox/Listbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,95 @@ describe('given multiple `true` Listbox', () => {
})
})

describe('given horizontal Listbox', () => {
const kbd = useKbd()
let wrapper: VueWrapper<InstanceType<typeof Listbox>>
let content: DOMWrapper<Element>
let items: DOMWrapper<Element>[]

window.HTMLElement.prototype.releasePointerCapture = vi.fn()
window.HTMLElement.prototype.hasPointerCapture = vi.fn()
window.HTMLElement.prototype.scrollIntoView = vi.fn()

beforeEach(() => {
document.body.innerHTML = ''
wrapper = mount(Listbox, { attachTo: document.body, props: { orientation: 'horizontal' } })
content = wrapper.find('[role=listbox]')
items = wrapper.findAll('[role=option]')
})

it('should pass axe accessibility tests', async () => {
expect(await axe(wrapper.element)).toHaveNoViolations()
})

describe('when focus on content', () => {
beforeEach(async () => {
await content.trigger('focus')
})

it('should pass the focus to the first item', () => {
expect(document.activeElement).toBe(items[0].element)
})

it('should have highlighted state on first item', () => {
expect(items[0].attributes('data-highlighted')).toBe('')
})

it('should emit `highlight` event', () => {
expect(wrapper.emitted('highlight')?.[0]?.[0]).toBeTruthy()
})

it('should highlight and select item when clicked', async () => {
const item = items[2]
await item.trigger('click')
expect(item.attributes('aria-selected')).toBe('true')
expect(item.attributes('data-state')).toBe('checked')
})

describe('after pressing `Enter`', async () => {
beforeEach(async () => {
await content.trigger('keydown', { key: kbd.ENTER })
})

it('should select the highlighted item', () => {
const item = items[0]
expect(item.attributes('data-highlighted')).toBe('')
expect(item.attributes('aria-selected')).toBe('true')
expect(item.attributes('data-state')).toBe('checked')
})

it('should emit `update:modelValue` event', () => {
expect(wrapper.emitted('update:modelValue')?.[0]?.[0]).toBe(items[0].text())
})

it('should deselect after pressing `Enter`', async () => {
await content.trigger('keydown', { key: kbd.ENTER })
const item = items[0]
expect(item.attributes('data-highlighted')).toBe('')
expect(item.attributes('aria-selected')).toBe('false')
expect(item.attributes('data-state')).toBe('unchecked')
})

describe('after selecting other item and press `Enter`', async () => {
beforeEach(async () => {
await content.trigger('keydown', { key: kbd.ARROW_RIGHT })
await content.trigger('keydown', { key: kbd.ARROW_RIGHT })
await content.trigger('keydown', { key: kbd.ENTER })
})

it('should select the third item', () => {
const item = items[0]
const newItem = items[2]
expect(item.attributes('aria-selected')).toBe('false')
expect(item.attributes('data-state')).toBe('unchecked')
expect(newItem.attributes('aria-selected')).toBe('true')
expect(newItem.attributes('data-state')).toBe('checked')
})
})
})
})
})

describe('given Listbox in a form', async () => {
let items: DOMWrapper<Element>[]

Expand Down
2 changes: 1 addition & 1 deletion packages/radix-vue/src/Listbox/ListboxContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const isClickFocus = refAutoReset(false, 10)
return
rootContext.onEnter(ev)
}"
@keydown.down.up.home.end.prevent="(event) => {
@keydown.down.up.left.right.home.end.prevent="(event) => {
rootContext.focusable.value ? rootContext.onKeydownNavigation(event) : undefined
}"
@keydown.enter="rootContext.onKeydownEnter"
Expand Down
2 changes: 1 addition & 1 deletion packages/radix-vue/src/Listbox/ListboxFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ onMounted(() => {
:disabled="rootContext.disabled.value ? '' : undefined"
:data-disabled="rootContext.disabled.value ? '' : undefined"
type="text"
@keydown.down.up.home.end.prevent="rootContext.onKeydownNavigation"
@keydown.down.up.left.right.home.end.prevent="rootContext.onKeydownNavigation"
@keydown.enter="rootContext.onKeydownEnter"
@input="(event: InputEvent) => {
modelValue = (event.target as HTMLInputElement).value
Expand Down
18 changes: 18 additions & 0 deletions packages/radix-vue/src/Listbox/story/ListboxChromatic.story.vue
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,23 @@ const multipleControl = ref()
</ListboxContent>
</ListboxRoot>
</Variant>

<Variant title="Orientation (Horizontal)">
<ListboxRoot
class="w-64 h-full flex flex-col p-1 rounded-lg border bg-white text-green9 mx-auto overflow-auto"
orientation="horizontal"
>
<ListboxContent class="flex flex-row ">
<ListboxItem
v-for="i in options"
:key="i"
:value="i"
class="w-full py-1 px-2 text-green9 select-none text-sm focus:ring-0 data-[highlighted]:outline-green9 data-[highlighted]:outline-1 data-[highlighted]:outline focus:outline-green9 data-[state=checked]:bg-green9 data-[state=checked]:text-white data-[disabled]:opacity-50 rounded"
>
{{ i }}
</ListboxItem>
</ListboxContent>
</ListboxRoot>
</Variant>
</Story>
</template>
Loading