Restore support for Select2 data format in custom select control (#4712)

Credit to @jimcottrell. Refs: #4413
This commit is contained in:
Jim Cottrell 2019-11-19 00:13:08 -07:00 committed by Ben Thomson
parent 1b4147212d
commit b5ed0b313a
4 changed files with 189 additions and 26 deletions

View File

@ -71,24 +71,68 @@ Use the `data-handler` attribute to source the select options from an AJAX handl
></select>
```
The AJAX handler should return results as an array.
The AJAX handler should return results in the [Select2 data format](https://select2.org/data-sources/formats).
```php
public function onGetOptions()
{
$results = [
[
'id' => 1,
'text' => 'Foobar',
],
...
return [
'results' => [
[
'id' => 1,
'text' => 'Foo'
],
[
'id' => 2,
'text' => 'Bar'
]
...
]
];
return ['result' => $results];
}
```
Due to the fact that JavaScript reorders numeric keys when interpreting the JSON data received by the AJAX handler, we suggest the method above for defining `results`. Support for the original `results` array format is however retained to ensure backwards compatibility.
Or a more full-featured example:
```php
public function onGetOptions()
{
return [
'results' => [
[
'id' => 1,
'text' => 'Foo',
'disabled' => true
],
[
'id' => 2,
'text' => 'Bar',
'selected' => true
],
[
'text' => 'Group',
'children' => [
[
'id' => 3,
'text' => 'Child 1'
],
[
'id' => 4,
'text' => 'Child 2'
]
...
]
]
...
],
'pagination' => [
'more' => true
]
];
}
```
The results array can be assigned to either the `result` or `results` key. As an alternative to the Select2 format, results can also be provided as an associative array (also assigned to either key). Due to the fact that JavaScript does not guarantee the order of object properties, we suggest the method above for defining results.
```php
public function onGetOptions()

View File

@ -87,23 +87,26 @@
return $request
},
processResults: function (data, params) {
var results = data.result;
var options = [];
var results = data.result || data.results,
options = []
for (var i in results) {
if (results.hasOwnProperty(i)) {
var isObject = i != null && i.constructor.name === 'Object';
options.push({
id: isObject ? results[i].id : i,
text: isObject ? results[i].text : results[i],
});
};
};
delete(data.result)
if (results[0] && typeof(results[0]) === 'object') { // Pass through Select2 format
options = results
}
else { // Key-value map
for (var i in results) {
if (results.hasOwnProperty(i)) {
options.push({
id: i,
text: results[i],
})
}
}
}
return {
results: options,
};
data.results = options
return data
},
dataType: 'json'
}

View File

@ -3514,7 +3514,12 @@ if($element.hasClass('select-hide-selected')){extraOptions.dropdownCssClass+=' s
var source=$element.data('handler');if(source){extraOptions.ajax={transport:function(params,success,failure){var $request=$element.request(source,{data:params.data})
$request.done(success)
$request.fail(failure)
return $request},processResults:function(data,params){var results=data.result;var options=[];for(var i in results){if(results.hasOwnProperty(i)){var isObject=i!=null&&i.constructor.name==='Object';options.push({id:isObject?results[i].id:i,text:isObject?results[i].text:results[i],});};};return{results:options,};},dataType:'json'}}
return $request},processResults:function(data,params){var results=data.result||data.results,options=[]
delete(data.result)
if(results[0]&&typeof(results[0])==='object'){options=results}
else{for(var i in results){if(results.hasOwnProperty(i)){options.push({id:i,text:results[i],})}}}
data.results=options
return data},dataType:'json'}}
var separators=$element.data('token-separators')
if(separators){extraOptions.tags=true
extraOptions.tokenSeparators=separators.split('|')

View File

@ -0,0 +1,111 @@
import { assert } from 'chai'
import fakeDom from 'helpers/fakeDom'
describe('modules/system/assets/ui/js/select.js', function () {
describe('AJAX processResults function', function () {
let dom,
window,
processResults,
keyValResultFormat = {
value1: 'text1',
value2: 'text2'
},
select2ResultFormat = [
{
id: 'value1',
text: 'text1',
disabled: true
},
{
id: 'value2',
text: 'text2',
selected: false
}
]
this.timeout(1000)
beforeEach((done) => {
// Load framework.js and select.js in the fake DOM
dom = fakeDom(`
<select class="custom-select" data-handler="onSearch"></select>
<script src="file://./node_modules/jquery/dist/jquery.js" id="jqueryScript"></script>
<script src="file://./modules/system/assets/js/framework.js" id="frameworkScript"></script>
<script src="file://./modules/system/assets/ui/js/select.js" id="selectScript"></script>
`)
window = dom.window
window.selectScript.onload = () => {
window.jQuery.fn.select2 = function(options) {
processResults = options.ajax.processResults
done()
}
}
})
afterEach(() => {
window.close()
})
it('supports a key-value mapping on the "result" key', function () {
let result = processResults({ result: keyValResultFormat })
assert.deepEqual(result, { results: [
{
id: 'value1',
text: 'text1'
},
{
id: 'value2',
text: 'text2'
}
]})
})
it('supports a key-value mapping on the "results" key', function() {
let result = processResults({ results: keyValResultFormat })
assert.deepEqual(result, { results: [
{
id: 'value1',
text: 'text1'
},
{
id: 'value2',
text: 'text2'
}
]})
})
it('passes through other data provided with key-value mapping', function() {
let result = processResults({ result: keyValResultFormat, other1: 1, other2: '2' })
assert.include(result, { other1: 1, other2: '2'})
})
it('supports the Select2 result format on the "result" key', function() {
let result = processResults({ result: select2ResultFormat })
assert.deepEqual(result, { results: select2ResultFormat })
})
it('passes through the Select2 result format on the "results" key', function() {
let result = processResults({ results: select2ResultFormat })
assert.deepEqual(result, { results: select2ResultFormat })
})
it('passes through other data provided with Select2 results format', function() {
let result = processResults({ results: select2ResultFormat, pagination: { more: true }, other: 'value' })
assert.deepInclude(result, { pagination: { more: true }, other: 'value' })
})
it('passes through the Select2 format with a group as the first entry', function() {
let data = [
{
text: 'Label',
children: select2ResultFormat
}
]
let result = processResults({ results: data })
assert.deepEqual(result, { results: data })
})
})
})