Skip to content

Commit

Permalink
fix(ld-select): add focus inner method for click on label
Browse files Browse the repository at this point in the history
  • Loading branch information
borisdiakur committed Nov 16, 2021
1 parent 5105304 commit 700da3b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 39 deletions.
50 changes: 29 additions & 21 deletions src/liquid/components/ld-select/ld-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
State,
Watch,
EventEmitter,
Method,
} from '@stencil/core'
import Tether from 'tether'
import { LdSelectPopper } from './ld-select-popper/ld-select-popper'
Expand All @@ -28,7 +29,7 @@ type SelectOption = { value: string; text: string }
styleUrl: 'ld-select.css',
shadow: true,
})
export class LdSelect {
export class LdSelect implements InnerFocusable {
@Element() el: HTMLElement

private selectRef!: HTMLElement
Expand Down Expand Up @@ -128,9 +129,9 @@ export class LdSelect {
handleTypeAhead(newQuery?: string) {
if (!newQuery) return

const options = (Array.from(
const options = Array.from(
this.listboxRef.querySelectorAll('ld-option-internal')
) as unknown) as LdOptionInternal[]
) as unknown as LdOptionInternal[]
const values = options.map((option) => option.value)
let index = values.findIndex(
(value) => value.toLowerCase().indexOf(newQuery.toLowerCase()) === 0
Expand Down Expand Up @@ -181,6 +182,14 @@ export class LdSelect {
@Event({ bubbles: true, cancelable: false, composed: true })
focusout: EventEmitter<string[]>

/** Sets focus on the trigger button. */
@Method()
async focusInner() {
if (!this.disabled) {
this.triggerRef.focus()
}
}

private isOverflowing() {
return (
this.selectionListRef.scrollHeight > this.selectionListRef.clientHeight
Expand Down Expand Up @@ -282,7 +291,7 @@ export class LdSelect {
}

private updatePopperShadowHeight() {
const ldPopper = (this.listboxRef as unknown) as LdSelectPopper
const ldPopper = this.listboxRef as unknown as LdSelectPopper
ldPopper.updateShadowHeight(
`calc(100% + ${this.triggerRef.getBoundingClientRect().height}px)`
)
Expand Down Expand Up @@ -338,9 +347,8 @@ export class LdSelect {
if (!initialized) {
children = this.el.querySelectorAll('ld-option')
} else {
children = this.internalOptionsContainerRef.querySelectorAll(
'ld-option-internal'
)
children =
this.internalOptionsContainerRef.querySelectorAll('ld-option-internal')
}

if (!children.length) {
Expand All @@ -351,7 +359,7 @@ export class LdSelect {

const selectedChildren = Array.from<HTMLElement>(children).filter(
(child) => {
return ((child as unknown) as LdOptionInternal).selected
return (child as unknown as LdOptionInternal).selected
}
)

Expand Down Expand Up @@ -466,7 +474,7 @@ export class LdSelect {
private clearSelection() {
Array.from(this.listboxRef.querySelectorAll('ld-option-internal')).forEach(
(option) => {
;((option as unknown) as LdOptionInternal).selected = false
;(option as unknown as LdOptionInternal).selected = false
}
)
this.selected = []
Expand All @@ -490,14 +498,14 @@ export class LdSelect {

if (!this.multiple) {
// Deselect currently selected option, if it's not the target option.
;((Array.from(
this.listboxRef.querySelectorAll('ld-option-internal')
) as unknown) as HTMLOptionElement[]).forEach((option) => {
;(
Array.from(
this.listboxRef.querySelectorAll('ld-option-internal')
) as unknown as HTMLOptionElement[]
).forEach((option) => {
if (
option !==
((target.closest(
'ld-option-internal'
) as unknown) as HTMLOptionElement)
(target.closest('ld-option-internal') as unknown as HTMLOptionElement)
) {
option.selected = false
}
Expand Down Expand Up @@ -539,12 +547,12 @@ export class LdSelect {
private handleEnd(ev) {
// Move focus to the last option.
ev.preventDefault()
const options = (Array.from(
const options = Array.from(
this.listboxRef.querySelectorAll('ld-option-internal')
) as unknown) as LdOptionInternal[]
) as unknown as LdOptionInternal[]
if (
document.activeElement !==
((options[options.length - 1] as unknown) as HTMLElement)
(options[options.length - 1] as unknown as HTMLElement)
) {
options[options.length - 1].focusOption()
}
Expand All @@ -563,7 +571,7 @@ export class LdSelect {
)
}
ldOption.focusOption()
const ldOptionHTMLEl = (ldOption as unknown) as HTMLElement
const ldOptionHTMLEl = ldOption as unknown as HTMLElement
if (!ldOptionHTMLEl.hasAttribute('selected')) {
ldOptionHTMLEl.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }))
}
Expand Down Expand Up @@ -670,8 +678,8 @@ export class LdSelect {
) {
this.selectAndFocus(
ev,
(document.activeElement
.previousElementSibling as unknown) as LdOptionInternal
document.activeElement
.previousElementSibling as unknown as LdOptionInternal
)
return
}
Expand Down
13 changes: 13 additions & 0 deletions src/liquid/components/ld-select/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,19 @@ The `ld-select` Web Component provides a low level API for integrating it with t
| `input` | Emitted with an array of selected values when an alteration to the selection is committed by the user. | `CustomEvent<string[]>` |


## Methods

### `focusInner() => Promise<void>`

Sets focus on the trigger button.

#### Returns

Type: `Promise<void>`




## Slots

| Slot | Description |
Expand Down
54 changes: 36 additions & 18 deletions src/liquid/components/ld-select/test/ld-select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ global.MutationObserver = TriggerableMutationObserver as MutationObserver

class FocusManager {
focus(el) {
const doc = (document as unknown) as { activeElement: Element }
const doc = document as unknown as { activeElement: Element }
doc.activeElement = el
}
}
Expand Down Expand Up @@ -58,8 +58,8 @@ async function getInternalOptions(page: SpecPage) {

function getShadow(page: SpecPage) {
const ldSelect = page.root
const doc = (document as unknown) as { activeElement: Element }
const shadowDoc = (ldSelect.shadowRoot as unknown) as {
const doc = document as unknown as { activeElement: Element }
const shadowDoc = ldSelect.shadowRoot as unknown as {
activeElement: Element
}
return {
Expand Down Expand Up @@ -101,9 +101,8 @@ describe('ld-select', () => {
'.ld-select-popper'
)
const slottedOptions = ldSelect.querySelectorAll('ld-option')
const internalOptions = ldSelectPopper.querySelectorAll(
'ld-option-internal'
)
const internalOptions =
ldSelectPopper.querySelectorAll('ld-option-internal')

expect(ldSelectEl.classList.contains('ld-select--expanded')).toBeTruthy()
expect(slottedOptions.length).toEqual(2)
Expand Down Expand Up @@ -211,9 +210,8 @@ describe('ld-select', () => {

const ldSelectPopper = await page.body.querySelector('ld-select-popper')

const internalOptions = ldSelectPopper.querySelectorAll(
'ld-option-internal'
)
const internalOptions =
ldSelectPopper.querySelectorAll('ld-option-internal')

expect(internalOptions.length).toEqual(2)

Expand Down Expand Up @@ -427,7 +425,7 @@ describe('ld-select', () => {

ldInternalOptions[0].focus = jest.fn(focusManager.focus)
ldInternalOptions[1].focus = jest.fn(focusManager.focus)
const doc = (document as unknown) as { activeElement: Element }
const doc = document as unknown as { activeElement: Element }

doc.activeElement = internalOptions[1]
ldInternalOptions[1].dispatchEvent(
Expand Down Expand Up @@ -474,9 +472,8 @@ describe('ld-select', () => {
await triggerPopperWithClick(page)

const ldSelectPopper = await page.body.querySelector('ld-select-popper')
const selectPopper = ldSelectPopper.shadowRoot.querySelector(
'.ld-select-popper'
)
const selectPopper =
ldSelectPopper.shadowRoot.querySelector('.ld-select-popper')

expect(
selectPopper.classList.contains('ld-select-popper--detached')
Expand Down Expand Up @@ -564,7 +561,7 @@ describe('ld-select', () => {
`,
})

const doc = (document as unknown) as { activeElement: Element }
const doc = document as unknown as { activeElement: Element }
const ldSelect = page.root
const btnTrigger = ldSelect.shadowRoot.querySelector(
'.ld-select__btn-trigger'
Expand All @@ -590,7 +587,7 @@ describe('ld-select', () => {
`,
})

const doc = (document as unknown) as { activeElement: Element }
const doc = document as unknown as { activeElement: Element }
const ldSelect = await page.root
const btnTrigger = ldSelect.shadowRoot.querySelector(
'.ld-select__btn-trigger'
Expand All @@ -617,7 +614,7 @@ describe('ld-select', () => {
`,
})

const doc = (document as unknown) as { activeElement: Element }
const doc = document as unknown as { activeElement: Element }
const ldSelect = await page.root
const btnTrigger = ldSelect.shadowRoot.querySelector(
'.ld-select__btn-trigger'
Expand Down Expand Up @@ -1806,7 +1803,7 @@ describe('ld-select', () => {
it('displays more indicator with maxRows prop set in multiple mode', async () => {
jest
.spyOn(
(LdSelect.prototype as unknown) as { isOverflowing },
LdSelect.prototype as unknown as { isOverflowing },
'isOverflowing'
)
.mockImplementation(() => true)
Expand Down Expand Up @@ -1848,7 +1845,7 @@ describe('ld-select', () => {

jest
.spyOn(
(LdSelect.prototype as unknown) as { isOverflowing },
LdSelect.prototype as unknown as { isOverflowing },
'isOverflowing'
)
.mockImplementation(() => false)
Expand Down Expand Up @@ -1992,4 +1989,25 @@ describe('ld-select', () => {

expect(page.body).toMatchSnapshot()
})

it('implements focus inner', async () => {
const page = await newSpecPage({
components,
html: `
<ld-select placeholder="Pick a fruit" name="fruit" popper-class="ld-select__popper--fruits">
<ld-option value="apple">Apple</ld-option>
<ld-option value="banana">Banana</ld-option>
</ld-select>
`,
})

const ldSelect = page.root
const btnTrigger = ldSelect.shadowRoot.querySelector(
'.ld-select__btn-trigger'
)
;(btnTrigger as HTMLElement).focus = jest.fn(focusManager.focus)

await page.root.focusInner()
expect((btnTrigger as HTMLElement).focus).toHaveBeenCalledTimes(1)
})
})

0 comments on commit 700da3b

Please sign in to comment.