Skip to content

Commit

Permalink
Combobox: Async - Attempt to convert response to an array if it's an …
Browse files Browse the repository at this point in the history
…object
  • Loading branch information
nicolas-jaussaud committed Nov 8, 2024
1 parent 0d95eb1 commit ea145f7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 23 deletions.
6 changes: 3 additions & 3 deletions assets/build/index.min.js

Large diffs are not rendered by default.

30 changes: 20 additions & 10 deletions assets/src/components/field/combo-box/async.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,45 @@ const getAsyncProps = props => {

/**
* Async search
*
*
* @see https://react-spectrum.adobe.com/react-aria/useComboBox.html#asynchronous-loading
*/
const list = useAsyncList({
async load({ filterText }) {

const data = {
...(props.asyncArgs ?? {}),
search: filterText
}

/**
* We support 2 ways to get async result, either by fetching a given url or by
* using our internal ajax module. If props.ajaxAction is defined, it means we rely
* using our internal ajax module. If props.ajaxAction is defined, it means we rely
* on our internal module
*
*
* @see https://docs.tangible.one/modules/plugin-framework/ajax/
*/
const results = props.ajaxAction
let results = props.ajaxAction
? await Tangible?.ajax(props.ajaxAction, data)
: await get(props.searchUrl ?? '', data)
: await get(props.searchUrl ?? '', data)

if ( results.length === 0 ) {
results.push({
id : '_noResults',
title : 'No results'
})
}

}

/**
* If response is an object, we attempt to use it anyway
*
* That can be usefull if value on the php side was an array with no consecutive numbers
* as it will be encoded as an json object
*/
if ( ! Array.isArray(results) && typeof results === 'object' ) {
results = Object.values(results).map(item => item)
}

const formatedResults = props.mapResults
? mapResults(results, props.mapResults)
: results
Expand All @@ -59,7 +69,7 @@ const getAsyncProps = props => {
/**
* We expect the response to be a list of object, and will use the id and title attribute
* as the list value/name
*
*
* Some endpoint will retrurn a different structure so we use the mapResults parameter to convert
* those response to the expected format
*/
Expand All @@ -79,7 +89,7 @@ const mapResults = (results, mapResults) => (
)

/**
* TODO: support nested objects
* TODO: support nested objects
*/
const mapResultsItem = (item, config) => (
typeof config === 'object'
Expand Down
69 changes: 59 additions & 10 deletions tests/jest/cases/components/controls/ComboBox.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../../../../../assets/src/index.jsx'
import {
import {
render,
screen,
within
Expand Down Expand Up @@ -39,7 +39,7 @@ describe('ComboBox component', () => {
label : 'Label',
choices : choices,
multiple : type === 'multiple'
}) }
}) }
<span>Click me to unfocus</span>
</>
)
Expand All @@ -48,10 +48,10 @@ describe('ComboBox component', () => {
expect(within(document).queryByText( choices[ value ] )).toBe(null)
}

await user.click(
await user.click(
type === 'multiple'
? within(container).getByText('Add')
: within(container).getByText('▼')
: within(container).getByText('▼')
)

for( const value in choices ) {
Expand All @@ -70,7 +70,7 @@ describe('ComboBox component', () => {
'single',
'multiple',
])('displays options in async mode when popover is open (%p)', async type => {

/**
* Used to simulate async response
*/
Expand Down Expand Up @@ -101,10 +101,10 @@ describe('ComboBox component', () => {
}
)

await user.click(
await user.click(
type === 'multiple'
? within(container).getByText('Add')
: within(container).getByText('▼')
: within(container).getByText('▼')
)

window.tangibleTests.fetchResponse.forEach(
Expand Down Expand Up @@ -132,7 +132,7 @@ describe('ComboBox component', () => {
* Used to simulate async response
*/
window.tangibleTests.fetchResponse = []

const user = userEvent.setup()
const { container } = render(
fields.render({
Expand All @@ -145,10 +145,10 @@ describe('ComboBox component', () => {
})
)

await user.click(
await user.click(
type === 'multiple'
? within(container).getByText('Add')
: within(container).getByText('▼')
: within(container).getByText('▼')
)

const item = within(document).getByText('No results')
Expand Down Expand Up @@ -194,4 +194,53 @@ describe('ComboBox component', () => {
expect(within(container).queryByText('x')).toBeFalsy()
}
})

test.each([
'single',
'multiple',
])('if response is an object, convert it to an array in async mode (%p)', async type => {

/**
* Used to simulate async response
*/
window.tangibleTests.fetchResponse = {
0 : { id: 'value1', title: 'Value 1' },
11 : { id: 'value2', title: 'Value 2' },
28 : { id: 'value3', title: 'Value 3' },
}

const user = userEvent.setup()
const { container } = render(
<>
{ fields.render({
name : 'field-name',
type : 'combo-box',
label : 'Label',
isAsync : true,
searchUrl : 'https://search.com/endpoint',
multiple : type === 'multiple'
}) }
<span>Click me to unfocus</span>
</>
)

Object.values(window.tangibleTests.fetchResponse).forEach(
result => {
expect(within(document).queryByText( result.title )).toBe(null)
}
)

await user.click(
type === 'multiple'
? within(container).getByText('Add')
: within(container).getByText('▼')
)

Object.values(window.tangibleTests.fetchResponse).forEach(
result => {
const item = within(document).getByText( result.title )
expect(item.getAttribute('data-key')).toBe( result.id )
}
)
})
})

0 comments on commit ea145f7

Please sign in to comment.