diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.js b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.js index e4e42eee1b4..d6003b6b0bd 100644 --- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.js +++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.js @@ -1154,6 +1154,39 @@ describe('Autocomplete component', () => { expect(on_type).toBeCalledTimes(4) }) + it('should have a button for screen readers to open options – regardless', () => { + const Comp = mount( + , + { 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(, { attachTo: attachToBody(), @@ -1161,9 +1194,9 @@ describe('Autocomplete component', () => { 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)' @@ -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') @@ -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( @@ -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', () => { @@ -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 @@ -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( + , + { + 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( { .getAttribute('data-test-id') ).toContain('bell') }) - - it('should have a button for screen readers to open options – regardless', () => { - const Comp = mount( - , - { 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', () => { diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js index 171f2e1f131..89639bc75b9 100644 --- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js +++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListProvider.js @@ -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() { @@ -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 }