Skip to content

Commit accdf02

Browse files
committed
fix(Autocomplete): properly handle non-string values for options
1 parent e424215 commit accdf02

File tree

2 files changed

+172
-4
lines changed

2 files changed

+172
-4
lines changed

js/src/autocomplete.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,15 +511,15 @@ class Autocomplete extends BaseComponent {
511511
_options.push({
512512
...customProperties,
513513
label,
514-
value,
514+
value: String(value),
515515
...isSelected && { selected: true },
516516
...option.disabled && { disabled: true }
517517
})
518518

519519
if (isSelected) {
520520
this._selected.push({
521521
label: option.label,
522-
value: String(option.label)
522+
value: String(value)
523523
})
524524
}
525525
}
@@ -749,7 +749,7 @@ class Autocomplete extends BaseComponent {
749749
}
750750
}
751751

752-
const value = String(element.dataset.value)
752+
const { value } = element.dataset
753753
const foundOption = this._findOptionByValue(value)
754754

755755
if (foundOption) {
@@ -808,7 +808,7 @@ class Autocomplete extends BaseComponent {
808808
}
809809

810810
_deselectOption(value) {
811-
this._selected = this._selected.filter(option => option.value !== String(value))
811+
this._selected = this._selected.filter(option => option.value !== value)
812812

813813
const option = SelectorEngine.findOne(`[data-value="${value}"]`, this._optionsElement)
814814
if (option) {

js/tests/unit/autocomplete.spec.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,4 +1055,172 @@ describe('Autocomplete', () => {
10551055
expect(autocomplete._inputElement.value).toBe('Option 1')
10561056
})
10571057
})
1058+
1059+
describe('number value handling', () => {
1060+
it('should convert number values to strings internally', () => {
1061+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1062+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1063+
const options = [
1064+
{ label: 'First Option', value: 1 },
1065+
{ label: 'Second Option', value: 2 },
1066+
{ label: 'Third Option', value: 3.5 }
1067+
]
1068+
const autocomplete = new Autocomplete(autocompleteEl, { options })
1069+
1070+
expect(autocomplete._options[0].value).toBe('1')
1071+
expect(autocomplete._options[1].value).toBe('2')
1072+
expect(autocomplete._options[2].value).toBe('3.5')
1073+
expect(typeof autocomplete._options[0].value).toBe('string')
1074+
})
1075+
1076+
it('should select option with number value converted to string', () => {
1077+
return new Promise(resolve => {
1078+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1079+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1080+
const autocomplete = new Autocomplete(autocompleteEl, {
1081+
options: [
1082+
{ label: 'Option 1', value: 1 },
1083+
{ label: 'Option 2', value: 2 }
1084+
]
1085+
})
1086+
1087+
autocompleteEl.addEventListener('changed.coreui.autocomplete', event => {
1088+
expect(event.value.label).toBe('Option 1')
1089+
expect(event.value.value).toBe('1')
1090+
expect(typeof event.value.value).toBe('string')
1091+
resolve()
1092+
})
1093+
1094+
const option = autocomplete._options[0]
1095+
autocomplete._selectOption(option)
1096+
})
1097+
})
1098+
1099+
it('should update input value when option with number value is selected', () => {
1100+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1101+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1102+
const autocomplete = new Autocomplete(autocompleteEl, {
1103+
options: [{ label: 'Option 1', value: 1 }]
1104+
})
1105+
1106+
const option = autocomplete._options[0]
1107+
autocomplete._selectOption(option)
1108+
1109+
expect(autocomplete._inputElement.value).toBe('Option 1')
1110+
expect(autocomplete._selected).toContain(option)
1111+
expect(autocomplete._selected[0].value).toBe('1')
1112+
expect(typeof autocomplete._selected[0].value).toBe('string')
1113+
})
1114+
1115+
it('should filter options with number values based on search term', () => {
1116+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1117+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1118+
const autocomplete = new Autocomplete(autocompleteEl, {
1119+
options: [
1120+
{ label: 'Apple', value: 1 },
1121+
{ label: 'Banana', value: 2 },
1122+
{ label: 'Cherry', value: 3 }
1123+
]
1124+
})
1125+
1126+
autocomplete.show()
1127+
autocomplete._search = 'app'
1128+
autocomplete._filterOptionsList()
1129+
1130+
const visibleOptions = Array.from(autocomplete._optionsElement.querySelectorAll('.autocomplete-option'))
1131+
.filter(option => option.style.display !== 'none')
1132+
1133+
expect(visibleOptions).toHaveSize(1)
1134+
expect(visibleOptions[0].textContent).toBe('Apple')
1135+
expect(visibleOptions[0].dataset.value).toBe('1')
1136+
})
1137+
1138+
it('should handle mixed string and number values by converting to strings', () => {
1139+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1140+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1141+
const options = [
1142+
{ label: 'String Option', value: 'string' },
1143+
{ label: 'Number Option', value: 42 },
1144+
{ label: 'Float Option', value: 3.14 },
1145+
{ label: 'Zero Option', value: 0 }
1146+
]
1147+
const autocomplete = new Autocomplete(autocompleteEl, { options })
1148+
1149+
expect(autocomplete._options[0].value).toBe('string')
1150+
expect(autocomplete._options[1].value).toBe('42')
1151+
expect(autocomplete._options[2].value).toBe('3.14')
1152+
expect(autocomplete._options[3].value).toBe('0')
1153+
expect(typeof autocomplete._options[0].value).toBe('string')
1154+
expect(typeof autocomplete._options[1].value).toBe('string')
1155+
expect(typeof autocomplete._options[2].value).toBe('string')
1156+
expect(typeof autocomplete._options[3].value).toBe('string')
1157+
})
1158+
1159+
it('should handle zero as a valid number value converted to string', () => {
1160+
return new Promise(resolve => {
1161+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1162+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1163+
const autocomplete = new Autocomplete(autocompleteEl, {
1164+
options: [{ label: 'Zero Option', value: 0 }]
1165+
})
1166+
1167+
autocompleteEl.addEventListener('changed.coreui.autocomplete', event => {
1168+
expect(event.value.value).toBe('0')
1169+
expect(typeof event.value.value).toBe('string')
1170+
resolve()
1171+
})
1172+
1173+
const option = autocomplete._options[0]
1174+
autocomplete._selectOption(option)
1175+
})
1176+
})
1177+
1178+
it('should handle negative number values converted to strings', () => {
1179+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1180+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1181+
const options = [
1182+
{ label: 'Negative Option', value: -1 },
1183+
{ label: 'Negative Float', value: -2.5 }
1184+
]
1185+
const autocomplete = new Autocomplete(autocompleteEl, { options })
1186+
1187+
expect(autocomplete._options[0].value).toBe('-1')
1188+
expect(autocomplete._options[1].value).toBe('-2.5')
1189+
expect(typeof autocomplete._options[0].value).toBe('string')
1190+
expect(typeof autocomplete._options[1].value).toBe('string')
1191+
})
1192+
1193+
it('should create autocomplete with number values in initial configuration', () => {
1194+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1195+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1196+
const config = {
1197+
value: 1,
1198+
options: [
1199+
{ label: 'Option 1', value: 1 },
1200+
{ label: 'Option 2', value: 2 }
1201+
]
1202+
}
1203+
const autocomplete = new Autocomplete(autocompleteEl, config)
1204+
1205+
expect(autocomplete._config.value).toBe(1)
1206+
expect(autocomplete._options[0].value).toBe('1')
1207+
expect(autocomplete._options[1].value).toBe('2')
1208+
})
1209+
1210+
it('should match options by string comparison even when passed number values', () => {
1211+
fixtureEl.innerHTML = '<div class="autocomplete"></div>'
1212+
const autocompleteEl = fixtureEl.querySelector('.autocomplete')
1213+
const autocomplete = new Autocomplete(autocompleteEl, {
1214+
value: 1,
1215+
options: [
1216+
{ label: 'Option 1', value: 1 },
1217+
{ label: 'Option 2', value: 2 }
1218+
]
1219+
})
1220+
1221+
expect(autocomplete._selected).toHaveSize(1)
1222+
expect(autocomplete._selected[0].value).toBe('1')
1223+
expect(autocomplete._selected[0].label).toBe('Option 1')
1224+
})
1225+
})
10581226
})

0 commit comments

Comments
 (0)