Skip to content

Commit

Permalink
feat(@dpc-sdp/ripple-ui-forms): add basic keyboard/search support to …
Browse files Browse the repository at this point in the history
…dropdowns
  • Loading branch information
David Featherston committed Jun 10, 2024
1 parent 974f4c3 commit 24cc4fd
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import RplFormDropDown from './RplFormDropdown.vue'
import { RplFormDropdownOptions } from './fixtures/sample'

const props = {
id: 'dropdown',
labelId: 'dropdown',
placeholder: 'Select',
options: RplFormDropdownOptions
}

describe('RplFormDropDown', () => {
it('mounts', () => {
cy.mount(RplFormDropDown, { props })

cy.get('.rpl-form-dropdown').should('be.visible')
})

it('can be toggled open and closed', () => {
cy.mount(RplFormDropDown, { props })

cy.get('.rpl-form-dropdown-input').click()
cy.get('.rpl-form-dropdown-menu').should('be.visible')
cy.get('.rpl-form-dropdown-input').click()
cy.get('.rpl-form-dropdown-menu').should('not.exist')
})

it('can be "searched" by typing from the input', () => {
cy.mount(RplFormDropDown, { props })

cy.get('.rpl-form-dropdown-input').type('b')
cy.focused().contains('Banana')

cy.get('.rpl-form-dropdown-input').type('bl')
cy.focused().contains('Blueberries')
})

it('can be "searched" by typing from an option', () => {
cy.mount(RplFormDropDown, { props })

cy.get('.rpl-form-dropdown-input').click()
cy.get('.rpl-form-dropdown-option').first().type('apr')
cy.focused().contains('Apricots')

cy.get('.rpl-form-dropdown-option').first().type('l')
cy.focused().contains('Lemon')
})

it('can be "traversed" by cycling through a single key stroke', () => {
cy.mount(RplFormDropDown, { props })

cy.get('.rpl-form-dropdown-input').type('a')
cy.focused().contains('Apple')

cy.focused().type('a')
cy.focused().contains('Apricots')

cy.focused().type('a')
cy.focused().contains('Avocado')

cy.focused().type('a')
cy.focused().contains('Apple')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import RplFormDropdown from './RplFormDropdown.vue'
import { RplButton } from '@dpc-sdp/ripple-ui-core/vue'
import StorybookInputFixture from './../StorybookInputFixture/StorybookInputFixture.vue'
import { RplFormDropdownOptions } from './fixtures/sample'
import '@dpc-sdp/ripple-ui-core/style/components'
import '../RplForm/RplForm.css'

Expand Down Expand Up @@ -74,11 +75,7 @@ export const SingleTemplate = (args) => ({
args={{
id: 'dropdown-single',
multiple: false,
options: [...Array(10).keys()].map((i) => ({
id: `item-${i + 1}`,
value: `item-${i + 1}`,
label: `Value ${i + 1}`
}))
options: RplFormDropdownOptions
}}
>
{SingleTemplate.bind()}
Expand Down Expand Up @@ -116,11 +113,7 @@ export const SingleTemplate = (args) => ({
args={{
id: 'dropdown-multi',
multiple: true,
options: [...Array(10).keys()].map((i) => ({
id: `item-${i + 1}`,
value: `item-${i + 1}`,
label: `Value ${i + 1}`
}))
options: RplFormDropdownOptions
}}
>
{SingleTemplate.bind()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default {

<script setup lang="ts">
import { computed, ref, watch, nextTick, inject } from 'vue'
import { onClickOutside } from '@vueuse/core'
import { onClickOutside, useDebounceFn } from '@vueuse/core'
import useFormkitFriendlyEventEmitter from '../../composables/useFormkitFriendlyEventEmitter'
import MultiValueLabel from './MultiValueLabel.vue'
import { useRippleEvent } from '@dpc-sdp/ripple-ui-core'
Expand All @@ -32,7 +32,7 @@ export interface RplFormDropdownProps {
}[]
maxItemsDisplayed?: number
pii?: boolean
unselectedValue: any
unselectedValue?: any
/**
* Only applicable when for single selects. If true, no 'placeholder' option
* is added and the user can't deselect a value, only choose a different value.
Expand Down Expand Up @@ -77,6 +77,7 @@ const containerRef = ref(null)
const inputRef = ref(null)
const menuRef = ref(null)
const optionRefs = ref([])
const searchCache = ref('')
const menuId = computed(() => `${props.id}__menu`)
Expand Down Expand Up @@ -127,6 +128,29 @@ const getUniqueOptionId = (optionId: string): string => {
return optionId ? `${props.id}-${optionId}` : ''
}
const processSearch = useDebounceFn(() => {
let options = [...(props.options || [])]
if (activeOptionId.value) {
const index = options.map((o) => o.id).indexOf(activeOptionId.value)
options = options.slice(index + 1).concat(options.slice(0, index + 1))
}
let found = options.find((o) =>
o.label.toLowerCase().startsWith(searchCache.value)
)
if (found) {
handleOpen()
activeOptionId.value = found.id
}
searchCache.value = ''
})
const handleSearch = (event: KeyboardEvent): void => {
if (event.key?.length === 1) {
searchCache.value = searchCache.value + event.key.toLowerCase()
processSearch()
}
}
const handleToggle = (fromKeyboard = false): void => {
if (isOpen.value) {
handleClose(fromKeyboard)
Expand Down Expand Up @@ -322,6 +346,7 @@ const hasValue = computed((): boolean => {
@keydown.esc.prevent="handleClose(true)"
@keydown.exact.tab="handleClose(false)"
@keydown.shift.tab="handleClose(false)"
@keydown.exact="handleSearch"
>
<div
v-bind="$attrs"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const RplFormDropdownOptions = [
{ id: 'apple', value: 'apple', label: 'Apple' },
{ id: 'banana', value: 'banana', label: 'Banana' },
{ id: 'orange', value: 'orange', label: 'Orange' },
{ id: 'strawberry', value: 'strawberry', label: 'Strawberry' },
{ id: 'apricots', value: 'apricots', label: 'Apricots' },
{ id: 'grapes', value: 'grapes', label: 'Grapes' },
{ id: 'pineapple', value: 'pineapple', label: 'Pineapple' },
{ id: 'sultana', value: 'sultana', label: 'Sultana' },
{ id: 'blueberries', value: 'blueberries', label: 'Blueberries' },
{ id: 'avocado', value: 'avocado', label: 'Avocado' },
{ id: 'peach', value: 'peach', label: 'Peach' },
{ id: 'lemon', value: 'lemon', label: 'Lemon' },
{ id: 'watermelon', value: 'watermelon', label: 'Watermelon' }
]

0 comments on commit 24cc4fd

Please sign in to comment.