diff --git a/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js b/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
index 65f8c246d25..56952f47fca 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
+++ b/packages/dnb-eufemia/src/components/autocomplete/Autocomplete.js
@@ -475,7 +475,6 @@ class AutocompleteInstance extends React.PureComponent {
clearTimeout(this._ariaLiveUpdateTimeout)
clearTimeout(this._focusTimeout)
clearTimeout(this._blurTimeout)
- clearTimeout(this._toggleVisibleTimeout)
}
setVisible = (args = null, onStateComplete = null) => {
@@ -564,6 +563,8 @@ class AutocompleteInstance extends React.PureComponent {
const data = this.runFilter(value, options)
const count = this.countData(data)
+ const { keep_value, keep_value_and_selection } = this.props
+
if (value && value.length > 0) {
// show the "no_options" message
if (count === 0) {
@@ -583,15 +584,12 @@ class AutocompleteInstance extends React.PureComponent {
}
}
} else {
- if (
- !isTrue(this.props.keep_value) &&
- !isTrue(this.props.keep_value_and_selection)
- ) {
+ if (!isTrue(keep_value) && !isTrue(keep_value_and_selection)) {
// this will not remove selected_item
this.totalReset()
}
- if (isTrue(this.props.keep_value)) {
+ if (isTrue(keep_value)) {
this.resetSelectedItem()
}
@@ -893,15 +891,38 @@ class AutocompleteInstance extends React.PureComponent {
}
}
- onReserveActivityHandler = (event) => {
- // Prevent to happen the on_blur event during drawer-list activity
- this.__preventFiringBlurEvent =
+ reserveActivityHandler = (event = null) => {
+ this.__preventFiringBlurEvent = Boolean(
event.key === 'enter' ||
- event.key === 'space' ||
- (event.target && getPreviousSibling('dnb-drawer-list', event.target))
+ (event?.currentTarget
+ ? getPreviousSibling('dnb-drawer-list', event.currentTarget) ||
+ getPreviousSibling(
+ 'dnb-input__submit-button__button',
+ event.currentTarget
+ )
+ : false)
+ )
+
+ if (this.__preventFiringBlurEvent) {
+ setTimeout(
+ () => {
+ this.__preventFiringBlurEvent = false
+ },
+ isTrue(this.props.no_animation) ? 1 : DrawerList.blurDelay
+ )
+ }
}
onBlurHandler = (event) => {
+ if (
+ this.__preventFiringBlurEvent ||
+ this.context.drawerList.hasFocusOnElement ||
+ this.state.hasBlur
+ ) {
+ this.__preventFiringBlurEvent = null
+ return false
+ }
+
const {
open_on_focus,
keep_value,
@@ -910,60 +931,54 @@ class AutocompleteInstance extends React.PureComponent {
no_animation,
} = this.props
- if (
- !this.state.hasBlur &&
- !this.__preventFiringBlurEvent &&
- !this.context.drawerList.hasFocusOnElement
- ) {
- dispatchCustomElementEvent(this, 'on_blur', {
- event,
- ...this.getEventObjects('on_blur'),
- })
+ dispatchCustomElementEvent(this, 'on_blur', {
+ event,
+ ...this.getEventObjects('on_blur'),
+ })
+
+ this.setState({
+ hasBlur: true,
+ hasFocus: false,
+ })
+ if (!isTrue(keep_value) && !isTrue(keep_value_and_selection)) {
this.setState({
- hasBlur: true,
- hasFocus: false,
+ typedInputValue: null,
+ _listenForPropChanges: false,
})
+ }
- if (!isTrue(keep_value_and_selection)) {
- this.setState({
- typedInputValue: null,
- _listenForPropChanges: false,
- })
- }
+ if (!isTrue(prevent_selection)) {
+ const existingValue = this.state.inputValue
- if (!isTrue(prevent_selection)) {
- const existingValue = this.state.inputValue
+ if (!isTrue(keep_value) && !isTrue(keep_value_and_selection)) {
this.clearInputValue()
+ }
- const resetAfterClose = () => {
- if (
- !isTrue(keep_value) ||
- !existingValue ||
- this.hasSelectedItem()
- ) {
- this.resetActiveItem()
- }
- this.resetFilter()
- }
-
- if (isTrue(no_animation)) {
- resetAfterClose()
- } else {
- clearTimeout(this._blurTimeout)
- this._blurTimeout = setTimeout(
- resetAfterClose,
- DrawerList.blurDelay
- ) // only to let the animation pass, before we make the effect. Else this would be a visible change
+ const resetAfterClose = () => {
+ if (
+ !isTrue(keep_value) ||
+ !existingValue ||
+ this.hasSelectedItem()
+ ) {
+ this.resetActiveItem()
}
+ this.resetFilter()
}
- if (isTrue(open_on_focus)) {
- this.setHidden()
+ if (isTrue(no_animation)) {
+ resetAfterClose()
+ } else {
+ clearTimeout(this._blurTimeout)
+ this._blurTimeout = setTimeout(
+ resetAfterClose,
+ DrawerList.blurDelay
+ ) // only to let the animation pass, before we make the effect. Else this would be a visible change
}
- } else if (this.__preventFiringBlurEvent) {
- this.__preventFiringBlurEvent = null
- return false
+ }
+
+ if (isTrue(open_on_focus)) {
+ this.setHidden()
}
}
@@ -1029,8 +1044,7 @@ class AutocompleteInstance extends React.PureComponent {
(!this.hasValidData() || !this.hasSelectedItem()) &&
!this.hasActiveItem()
) {
- clearTimeout(this._toggleVisibleTimeout)
- this._toggleVisibleTimeout = setTimeout(this.toggleVisible, 1) // to make sure we first handle the DrawerList key enter, before we update the state with a toggle/visible. Else the submit is not set properly
+ this.toggleVisible()
} else {
this.setVisible()
}
@@ -1541,12 +1555,9 @@ class AutocompleteInstance extends React.PureComponent {
} catch (e) {
// do nothing
}
- clearTimeout(this._focusTimeout)
- this._focusTimeout = setTimeout(() => {
- this.setState({
- hasFocus: false,
- })
- }, 1) // we have to wait in order to make sure the focus situation is cleared up
+ this.setState({
+ hasFocus: false,
+ })
}
)
}
@@ -1895,6 +1906,7 @@ class AutocompleteInstance extends React.PureComponent {
status: !opened && status ? status_state : null,
onKeyDown: this.onTriggerKeyDownHandler,
onSubmit: this.toggleVisible,
+ onMouseDown: this.reserveActivityHandler,
'aria-haspopup': 'listbox',
'aria-expanded': isExpanded,
'aria-label': !hidden ? submit_button_title : undefined,
@@ -2057,8 +2069,8 @@ class AutocompleteInstance extends React.PureComponent {
on_select={this.onSelectHandler}
on_hide={this.onHideHandler}
on_pre_change={this.onPreChangeHandler}
- on_key_down={this.onReserveActivityHandler}
- onMouseDown={this.onReserveActivityHandler}
+ on_key_down={this.reserveActivityHandler}
+ onMouseDown={this.reserveActivityHandler}
independent_width={independent_width}
/>
diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
index 08b43b27eda..4caaa05851f 100644
--- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
+++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/Autocomplete.test.tsx
@@ -238,7 +238,7 @@ describe('Autocomplete component', () => {
assertInputValue()
// open
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(
document
@@ -494,32 +494,28 @@ describe('Autocomplete component', () => {
).toBe('')
// simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(
document.querySelector('.dnb-sr-only:not([hidden])').textContent
).toBe('AA c')
// simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(
document.querySelector('.dnb-sr-only:not([hidden])').textContent
).toBe('BB cc zethx')
// simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(
document.querySelector('.dnb-sr-only:not([hidden])').textContent
).toBe('CCcc')
act(() => {
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
+ dispatchKeyDown(13) // enter
})
expect(
@@ -528,7 +524,7 @@ describe('Autocomplete component', () => {
// simulate changes
toggle()
- keydown(38) // up
+ keyDownOnInput(38) // up
expect(
document.querySelector('.dnb-sr-only:not([hidden])').textContent
@@ -540,7 +536,7 @@ describe('Autocomplete component', () => {
})
// simulate changes
- keydown(38) // up
+ keyDownOnInput(38) // up
expect(
document.querySelector('.dnb-sr-only:not([hidden])').textContent
@@ -769,14 +765,14 @@ describe('Autocomplete component', () => {
).not.toBeInTheDocument()
// then simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(optionElements()[0].classList).toContain(
'dnb-drawer-list__option--focus'
)
// then simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(optionElements()[1].classList).toContain(
'dnb-drawer-list__option--focus'
@@ -816,7 +812,7 @@ describe('Autocomplete component', () => {
// remove selection and reset the order and open again
// aria-selected should now be on place 1
- keydown(27) // esc
+ keyDownOnInput(27) // esc
toggle()
elem = optionElements()[1]
@@ -849,7 +845,7 @@ describe('Autocomplete component', () => {
it('has correct "aria-expanded"', () => {
render()
- keydown(13) // enter
+ keyDownOnInput(13) // enter
const elem = document.querySelector('.dnb-autocomplete')
expect(
@@ -957,7 +953,7 @@ describe('Autocomplete component', () => {
ulElement: null,
})
- keydown(27) // esc
+ keyDownOnInput(27) // esc
expect(on_hide).toHaveBeenCalledTimes(1)
expect(on_hide.mock.calls[0][0].attributes).toMatchObject(params)
expect(on_hide.mock.calls[0][0].event).toEqual(
@@ -980,7 +976,7 @@ describe('Autocomplete component', () => {
document.querySelector('.dnb-autocomplete').classList
).toContain('dnb-autocomplete--opened')
- keydown(27) // esc
+ keyDownOnInput(27) // esc
expect(
document.querySelector('.dnb-autocomplete').classList
@@ -1090,67 +1086,6 @@ describe('Autocomplete component', () => {
).toBe('')
})
- it('returns correct value in on_blur event', () => {
- const on_focus = jest.fn()
- const on_blur = jest.fn()
- const onBlur = jest.fn()
- const on_change = jest.fn()
-
- render(
-
- )
-
- fireEvent.focus(document.querySelector('input'))
- expect(on_focus).toHaveBeenCalledTimes(1)
-
- fireEvent.change(document.querySelector('input'), {
- target: { value: 'cc' },
- })
-
- // Try to call on_blur by mousedown
- fireEvent.mouseDown(document.querySelector('.dnb-drawer-list'))
- fireEvent.blur(document.querySelector('input'))
- expect(on_blur).toHaveBeenCalledTimes(0)
- expect(onBlur).toHaveBeenCalledTimes(0)
-
- // Try to call on_blur by keystroke
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
- fireEvent.blur(document.querySelector('input'))
- expect(on_blur).toHaveBeenCalledTimes(0)
- expect(onBlur).toHaveBeenCalledTimes(0)
-
- // Make a selection
- fireEvent.click(
- document.querySelectorAll('li.dnb-drawer-list__option')[1]
- )
-
- expect(on_change).toHaveBeenCalledTimes(1)
- expect(on_change.mock.calls[0][0].data).toBe('BB cc zethx')
-
- // All the clicks should not have invoked the on_blur event
- expect(on_blur).toHaveBeenCalledTimes(0)
- expect(onBlur).toHaveBeenCalledTimes(0)
-
- // But a second one will
- fireEvent.blur(document.querySelector('input'))
-
- expect(on_blur).toHaveBeenCalledTimes(1)
- expect(onBlur).toHaveBeenCalledTimes(1)
- expect(on_blur.mock.calls[0][0].value).toBe('BB cc zethx')
- expect(onBlur.mock.calls[0][0].value).toBe('BB cc zethx')
- })
-
it('will invalidate selected_item when selected_key changes', () => {
const mockData = [
{ selected_key: 'a', content: 'AA c' },
@@ -1432,13 +1367,9 @@ describe('Autocomplete component', () => {
const openAndSelectNext = () => {
// then simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
act(() => {
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
+ dispatchKeyDown(13) // enter
})
}
@@ -1557,7 +1488,7 @@ describe('Autocomplete component', () => {
})
// Make first item active
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
@@ -1571,7 +1502,7 @@ describe('Autocomplete component', () => {
expect(focusElement()).not.toBeInTheDocument()
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
@@ -1585,12 +1516,8 @@ describe('Autocomplete component', () => {
target: { value: 'cc' },
})
- keydown(40) // activate
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
+ keyDownOnInput(40) // activate
+ dispatchKeyDown(13) // enter
closeAndReopen()
@@ -1658,7 +1585,7 @@ describe('Autocomplete component', () => {
})
// Make first item active
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
@@ -1672,7 +1599,7 @@ describe('Autocomplete component', () => {
expect(focusElement()).not.toBeInTheDocument()
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
@@ -1686,12 +1613,8 @@ describe('Autocomplete component', () => {
target: { value: 'cc' },
})
- keydown(40) // activate
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
+ keyDownOnInput(40) // activate
+ dispatchKeyDown(13) // enter
closeAndReopen()
@@ -1740,7 +1663,9 @@ describe('Autocomplete component', () => {
/>
)
- const inputElement = document.querySelector('.dnb-input__input')
+ const inputElement = document.querySelector(
+ '.dnb-input__input'
+ ) as HTMLInputElement
const optionElements = () =>
document.querySelectorAll('li.dnb-drawer-list__option')
const focusElement = () =>
@@ -1759,13 +1684,15 @@ describe('Autocomplete component', () => {
})
// Make first item active
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
+ expect(inputElement.value).toBe('cc')
closeAndReopen()
expect(focusElement()).not.toBeInTheDocument()
+ expect(inputElement.value).toBe('cc')
fireEvent.change(inputElement, {
target: { value: '' },
@@ -1773,7 +1700,7 @@ describe('Autocomplete component', () => {
expect(focusElement()).not.toBeInTheDocument()
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(focusElement()).toBeInTheDocument()
@@ -1781,25 +1708,22 @@ describe('Autocomplete component', () => {
// This here is what we expect
expect(focusElement()).not.toBeInTheDocument()
+ expect(inputElement.value).toBe('')
// This also opens the drawer-list
fireEvent.change(inputElement, {
target: { value: 'cc' },
})
- keydown(40) // activate
- document.dispatchEvent(
- new KeyboardEvent('keydown', {
- keyCode: 13, // enter
- })
- )
+ keyDownOnInput(40) // activate
+ dispatchKeyDown(13) // enter
closeAndReopen()
// Now we have a selected item
expect(selectedElement()).toBeInTheDocument()
expect(focusElement()).toBeInTheDocument()
- expect((inputElement as HTMLInputElement).value).toBe('CC cc')
+ expect(inputElement.value).toBe('CC cc')
fireEvent.change(inputElement, {
target: { value: '' },
@@ -1810,6 +1734,7 @@ describe('Autocomplete component', () => {
// This here is what we expect
expect(focusElement()).toBeInTheDocument()
expect(selectedElement()).toBeInTheDocument()
+ expect(inputElement.value).toBe('')
expect(on_show).toBeCalledTimes(2)
expect(on_hide).toBeCalledTimes(2)
@@ -1912,7 +1837,7 @@ describe('Autocomplete component', () => {
).toContain('dnb-autocomplete--opened')
// close
- keydown(27) // esc
+ keyDownOnInput(27) // esc
expect(
document.querySelector('.dnb-autocomplete').classList
@@ -1976,7 +1901,7 @@ describe('Autocomplete component', () => {
act(() => {
// close
- document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 27 }))
+ dispatchKeyDown(27)
})
expect(on_hide).toHaveBeenCalledTimes(1)
@@ -1995,7 +1920,7 @@ describe('Autocomplete component', () => {
// close again, but with false returned
act(() => {
- document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 27 }))
+ dispatchKeyDown(27)
})
expect(on_hide).toHaveBeenCalledTimes(2)
@@ -2040,7 +1965,7 @@ describe('Autocomplete component', () => {
)
- keydown(40) // down
+ keyDownOnInput(40) // down
fireEvent.change(document.querySelector('.dnb-input__input'), {
target: { value: 'aa' },
@@ -2080,7 +2005,7 @@ describe('Autocomplete component', () => {
/>
)
- keydown(40) // down
+ keyDownOnInput(40) // down
fireEvent.change(document.querySelector('.dnb-input__input'), {
target: { value: 'aa' },
@@ -2206,7 +2131,7 @@ describe('Autocomplete component', () => {
toggle()
// then simulate changes
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(
(document.querySelector('.dnb-input__input') as HTMLInputElement)
@@ -2350,10 +2275,10 @@ describe('Autocomplete component', () => {
fireEvent.focus(document.querySelector('input'))
// focus the first item
- keydown(40) // down
+ keyDownOnInput(40) // down
// focus the second item
- keydown(40) // down
+ keyDownOnInput(40) // down
await userEvent.tab()
@@ -2376,7 +2301,7 @@ describe('Autocomplete component', () => {
document.querySelector('input').focus()
// open
- keydown(40) // down
+ keyDownOnInput(40) // down
expect(document.activeElement.tagName).toBe('INPUT')
})
@@ -2616,6 +2541,350 @@ describe('Autocomplete component', () => {
expect(inputElement.value).toEqual('CH (+41)')
})
+
+ describe('input blur', () => {
+ const mainElement = () => document.querySelector('.dnb-autocomplete')
+ const inputElement = () => document.querySelector('.dnb-input__input')
+ const inputComponent = () => document.querySelector('.dnb-input')
+ const listElement = () =>
+ document.querySelector('.dnb-autocomplete__list')
+ const optionElement = () =>
+ document.querySelector('li.dnb-drawer-list__option')
+ const focusElement = () =>
+ document.querySelector('li.dnb-drawer-list__option--focus')
+ const selectedElement = () =>
+ document.querySelector('li.dnb-drawer-list__option--selected')
+
+ it('shold emit with empty value', async () => {
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+
+ render(
+
+ )
+
+ await userEvent.type(inputElement(), '{Enter}')
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).not.toBeInTheDocument()
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ fireEvent.blur(inputElement())
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputElement()).toHaveValue('')
+ expect(inputComponent()).toHaveAttribute(
+ 'data-input-state',
+ 'initial'
+ )
+ expect(onBlur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenLastCalledWith(
+ expect.objectContaining({ value: '' })
+ )
+ })
+
+ it('shold not emit on submit button press', () => {
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+
+ render(
+
+ )
+
+ const submitElement = () =>
+ document.querySelector(
+ 'button.dnb-input__submit-button__button:not(.dnb-input__clear-button)'
+ )
+
+ fireEvent.focus(inputElement())
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+
+ fireEvent.click(submitElement())
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+
+ fireEvent.click(submitElement())
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ fireEvent.blur(inputElement())
+
+ expect(inputComponent()).toHaveAttribute(
+ 'data-input-state',
+ 'initial'
+ )
+ expect(onBlur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenLastCalledWith(
+ expect.objectContaining({ value: '' })
+ )
+ })
+
+ it('should include custom input value and not emit on input enter key', () => {
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+
+ render(
+
+ )
+
+ fireEvent.focus(inputElement())
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+
+ fireEvent.change(inputElement(), {
+ target: { value: 'invalid' },
+ })
+
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).not.toBeInTheDocument()
+ expect(selectedElement()).not.toBeInTheDocument()
+
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(optionElement()).not.toBeInTheDocument()
+ expect(focusElement()).not.toBeInTheDocument()
+ expect(selectedElement()).not.toBeInTheDocument()
+ expect(inputElement()).toHaveValue('invalid')
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ fireEvent.blur(inputElement())
+
+ expect(onBlur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ value: 'invalid',
+ dataList: [
+ expect.objectContaining({ content: 'Ingen alternativer' }),
+ ],
+ })
+ )
+ expect(inputComponent()).toHaveAttribute(
+ 'data-input-state',
+ 'initial'
+ )
+ })
+
+ it('should not emit on item selection with enter key', async () => {
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+
+ render(
+
+ )
+
+ fireEvent.focus(inputElement())
+ keyDownOnInput(13) // enter
+ keyDownOnInput(40) // down
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).toBeInTheDocument()
+ expect(selectedElement()).not.toBeInTheDocument()
+
+ fireEvent.keyDown(listElement(), {
+ keyCode: 13, // enter
+ })
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputElement()).toHaveValue('AA c')
+
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).toBeInTheDocument()
+ expect(selectedElement()).toBeInTheDocument()
+
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+
+ await wait(1)
+
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ fireEvent.blur(inputElement())
+
+ expect(onBlur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ value: 'AA c',
+ dataList: [
+ expect.objectContaining({ content: 'AA c' }),
+ expect.anything(),
+ expect.anything(),
+ ],
+ })
+ )
+ expect(inputComponent()).toHaveAttribute(
+ 'data-input-state',
+ 'initial'
+ )
+ })
+
+ it('should not emit on item selection with mouse click', async () => {
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+
+ render(
+
+ )
+
+ fireEvent.focus(inputElement())
+ keyDownOnInput(13) // enter
+ keyDownOnInput(40) // down
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).toBeInTheDocument()
+ expect(selectedElement()).not.toBeInTheDocument()
+
+ fireEvent.click(focusElement())
+
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+ expect(inputElement()).toHaveValue('AA c')
+
+ keyDownOnInput(13) // enter
+
+ expect(mainElement().classList).toContain('dnb-autocomplete--opened')
+ expect(optionElement()).toBeInTheDocument()
+ expect(focusElement()).toBeInTheDocument()
+ expect(selectedElement()).toBeInTheDocument()
+
+ keyDownOnInput(13) // enter
+
+ await wait(1)
+
+ expect(mainElement().classList).not.toContain(
+ 'dnb-autocomplete--opened'
+ )
+ expect(inputComponent()).toHaveAttribute('data-input-state', 'focus')
+ expect(onBlur).toHaveBeenCalledTimes(0)
+ expect(on_blur).toHaveBeenCalledTimes(0)
+
+ fireEvent.blur(inputElement())
+
+ expect(onBlur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenCalledTimes(1)
+ expect(on_blur).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ value: 'AA c',
+ dataList: [
+ expect.objectContaining({ content: 'AA c' }),
+ expect.anything(),
+ expect.anything(),
+ ],
+ })
+ )
+ expect(inputComponent()).toHaveAttribute(
+ 'data-input-state',
+ 'initial'
+ )
+ })
+
+ it('should dismiss focus only on blur', () => {
+ const on_focus = jest.fn()
+ const on_blur = jest.fn()
+ const onBlur = jest.fn()
+ const on_change = jest.fn()
+
+ render(
+
+ )
+
+ expect(document.querySelector('.dnb-input')).toHaveAttribute(
+ 'data-input-state',
+ 'virgin'
+ )
+
+ fireEvent.focus(document.querySelector('input'))
+
+ fireEvent.keyDown(document.querySelector('input'), {
+ key: 'Enter',
+ keyCode: 13,
+ })
+
+ expect(document.querySelector('.dnb-input')).toHaveAttribute(
+ 'data-input-state',
+ 'focus'
+ )
+
+ fireEvent.keyDown(document.querySelector('input'), {
+ key: 'Enter',
+ keyCode: 13,
+ })
+ })
+ })
})
describe('Autocomplete markup', () => {
@@ -2660,12 +2929,20 @@ describe('Autocomplete scss', () => {
})
})
-const keydown = (keyCode) => {
+const keyDownOnInput = (keyCode) => {
fireEvent.keyDown(document.querySelector('.dnb-input__input'), {
keyCode,
})
}
+const dispatchKeyDown = (keyCode) => {
+ document.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ keyCode,
+ })
+ )
+}
+
const toggle = () => {
fireEvent.click(
document.querySelector(