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(Autocomplete): fix using tab + write issue #1473

Merged
merged 1 commit into from
Jun 22, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -1154,16 +1154,49 @@ describe('Autocomplete component', () => {
expect(on_type).toBeCalledTimes(4)
})

it('should have a button for screen readers to open options – regardless', () => {
const Comp = mount(
<Component id="autocomplete-id" data={mockData} no_animation />,
{ attachTo: attachToBody() }
)

const buttonElem = Comp.find('.dnb-sr-only').find('button')

expect(buttonElem.text()).toBe(
'Bla gjennom alternativer, lukk med esc knappen'
)
expect(buttonElem.exists()).toBe(true)
expect(buttonElem.instance().getAttribute('tabindex')).toBe('-1')

buttonElem.simulate('click')

expect(
Comp.find('.dnb-autocomplete').hasClass('dnb-autocomplete--opened')
).toBe(true)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-drawer-list__options'
)

buttonElem.simulate('click')

expect(
Comp.find('.dnb-autocomplete').hasClass('dnb-autocomplete--opened')
).toBe(false)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-input__input'
)
})

it('should keep input focus when using show-all or select item', () => {
const Comp = mount(<Component data={mockData} {...mockProps} />, {
attachTo: attachToBody(),
})

Comp.find('input').simulate('change', { target: { value: 'cc' } })

expect(
document.activeElement.classList.contains('dnb-drawer-list__options')
).toBe(true)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-input__input'
)
expect(
Comp.find(
'li.dnb-drawer-list__option:not(.dnb-autocomplete__show-all)'
Expand All @@ -1172,9 +1205,9 @@ describe('Autocomplete component', () => {

Comp.find('input').instance().focus()

expect(
document.activeElement.classList.contains('dnb-input__input')
).toBe(true)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-input__input'
)

Comp.find('li.dnb-autocomplete__show-all').simulate('click')

Expand All @@ -1184,9 +1217,9 @@ describe('Autocomplete component', () => {
.hasClass('dnb-drawer-list__option--focus')
).toBe(true)

expect(
document.activeElement.classList.contains('dnb-input__input')
).toBe(true)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-input__input'
)

expect(
Comp.find(
Expand All @@ -1197,9 +1230,9 @@ describe('Autocomplete component', () => {
Comp.find('input').instance().blur()
Comp.find('li.dnb-drawer-list__option').at(0).simulate('click')

expect(
document.activeElement.classList.contains('dnb-input__input')
).toBe(true)
expect(Array.from(document.activeElement.classList)).toContain(
'dnb-input__input'
)
})

it('will open drawer-list when open_on_focus is set to true', () => {
Expand Down Expand Up @@ -1583,10 +1616,6 @@ describe('Autocomplete component', () => {
// open
keydown(Comp, 40) // down

expect(Array.from(document.activeElement.classList)).toContain(
'dnb-drawer-list__options'
)

Comp.find('input').instance().focus()

// focus the first item
Expand Down Expand Up @@ -1622,6 +1651,24 @@ describe('Autocomplete component', () => {
runTabs()
})

it('will keep focus on input when opening', () => {
const mockData = ['first item', 'one more item']

const Comp = mount(
<Component id="autocomplete-id" data={mockData} {...mockProps} />,
{
attachTo: attachToBody(),
}
)

Comp.find('input').instance().focus()

// open
keydown(Comp, 40) // down

expect(document.activeElement.tagName).toBe('INPUT')
})

it('submit_element will replace the internal SubmitButton', () => {
const Comp = mount(
<Component
Expand Down Expand Up @@ -1658,36 +1705,6 @@ describe('Autocomplete component', () => {
.getAttribute('data-test-id')
).toContain('bell')
})

it('should have a button for screen readers to open options – regardless', () => {
const Comp = mount(
<Component id="autocomplete-id" data={mockData} no_animation />,
{ attachTo: attachToBody() }
)

const buttonElem = Comp.find('.dnb-sr-only').find('button')

expect(buttonElem.exists()).toBe(true)
expect(buttonElem.instance().getAttribute('tabindex')).toBe('-1')

buttonElem.simulate('click')

expect(
Comp.find('.dnb-autocomplete').hasClass('dnb-autocomplete--opened')
).toBe(true)
expect(
document.activeElement.classList.contains('dnb-drawer-list__options')
).toBe(true)

buttonElem.simulate('click')

expect(
Comp.find('.dnb-autocomplete').hasClass('dnb-autocomplete--opened')
).toBe(false)
expect(
document.activeElement.classList.contains('dnb-input__input')
).toBe(true)
})
})

describe('Autocomplete markup', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,63 +520,57 @@ export default class DrawerListProvider extends React.PureComponent {
}, 1) // to make sure we are after all DOM updates, else we don't get this scrolling
}

/**
* During opening (Dropdown, Autocomplete),
* and if noting is selected,
* set scroll to item.
*
* @param {number} active_item The item to set as active
* @param {object} param1
* @property {boolean} fireSelectEvent Wheter the onSelect event should get emitted
* @property {boolean} scrollTo Wheter the list should scroll to the new active item nor not
* @property {event} event The event object to forward to the emitted events
*/
setActiveItemAndScrollToIt = (
active_item,
{ fireSelectEvent = false, scrollTo = true, event = null } = {}
) => {
// during opening, and if noting is selected, set focus and scroll to item
if (parseFloat(active_item) === -1) {
this.setState(
{
active_item: -1,
},
() => {
if (this._refUl.current) {
this._refUl.current.focus({ preventScroll: true })
}
dispatchCustomElementEvent(this, 'on_show_focus', {
element: this._refUl.current,
})
this.setState({ active_item }, () => {
if (parseFloat(active_item) === -1) {
// Select the first item to NVDA is more easily navigateable,
// without using the alt + arrow key
// else we set the focus on the "ul" element
if (document.activeElement?.tagName !== 'INPUT') {
this._refUl.current?.focus({ preventScroll: true })
}
)

return // stop here
}

if (parseFloat(active_item) > -1) {
this.setState(
{
active_item,
},
() => {
const { selected_item } = this.state

if (fireSelectEvent) {
const attributes = this.attributes
const ret = dispatchCustomElementEvent(
this.state,
'on_select',
{
active_item,
value: getSelectedItemValue(selected_item, this.state),
data: getEventData(active_item, this.state.data),
event,
attributes,
}
)
if (ret === false) {
return // stop here!
}
}

if (isTrue(this.props.no_animation)) {
scrollTo = false
dispatchCustomElementEvent(this, 'on_show_focus', {
element: this._refUl.current,
})
} else if (parseFloat(active_item) > -1) {
const { selected_item } = this.state

if (fireSelectEvent) {
const attributes = this.attributes
const ret = dispatchCustomElementEvent(this.state, 'on_select', {
active_item,
value: getSelectedItemValue(selected_item, this.state),
data: getEventData(active_item, this.state.data),
event,
attributes,
})
if (ret === false) {
return // stop here!
}
}

this.scrollToItem(active_item, { scrollTo })
if (isTrue(this.props.no_animation)) {
scrollTo = false
}
)
}

this.scrollToItem(active_item, { scrollTo })
}
})
}

removeDirectionObserver() {
Expand Down Expand Up @@ -1087,9 +1081,6 @@ export default class DrawerListProvider extends React.PureComponent {
ulElement: this._refUl.current,
})

// Select the first item to NVDA is more easily navigateable,
// without using the alt + arrow key
// else we set the focus on the "ul" element
this.setActiveItemAndScrollToIt(
parseFloat(active_item) > -1 ? active_item : -1,
{ scrollTo: false }
Expand Down