Skip to content

Commit bcf6f2c

Browse files
committed
[Autocomplete] Avoid destroying on disconnect
1 parent f63cb59 commit bcf6f2c

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed

src/Autocomplete/assets/dist/controller.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,28 @@ class default_1 extends Controller {
6262
}
6363
disconnect() {
6464
this.stopMutationObserver();
65+
let currentSelectedValues = [];
66+
if (this.selectElement) {
67+
if (this.selectElement.multiple) {
68+
currentSelectedValues = Array.from(this.selectElement.options)
69+
.filter((option) => option.selected)
70+
.map((option) => option.value);
71+
}
72+
else {
73+
currentSelectedValues = [this.selectElement.value];
74+
}
75+
}
6576
this.tomSelect.destroy();
77+
if (this.selectElement) {
78+
if (this.selectElement.multiple) {
79+
Array.from(this.selectElement.options).forEach((option) => {
80+
option.selected = currentSelectedValues.includes(option.value);
81+
});
82+
}
83+
else {
84+
this.selectElement.value = currentSelectedValues[0];
85+
}
86+
}
6687
}
6788
getMaxOptions() {
6889
return this.selectElement ? this.selectElement.options.length : 50;

src/Autocomplete/assets/src/controller.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,35 @@ export default class extends Controller {
8686

8787
disconnect() {
8888
this.stopMutationObserver();
89+
90+
// TomSelect.destroy() resets the element to its original HTML. This
91+
// causes the selected value to be lost. We store it.
92+
let currentSelectedValues: string[] = [];
93+
if (this.selectElement) {
94+
if (this.selectElement.multiple) {
95+
// For multiple selects, store the array of selected values
96+
currentSelectedValues = Array.from(this.selectElement.options)
97+
.filter((option) => option.selected)
98+
.map((option) => option.value);
99+
} else {
100+
// For single select, store the single value
101+
currentSelectedValues = [this.selectElement.value];
102+
}
103+
}
104+
89105
this.tomSelect.destroy();
106+
107+
if (this.selectElement) {
108+
if (this.selectElement.multiple) {
109+
// Restore selections for multiple selects
110+
Array.from(this.selectElement.options).forEach((option) => {
111+
option.selected = currentSelectedValues.includes(option.value);
112+
});
113+
} else {
114+
// Restore selection for single select
115+
this.selectElement.value = currentSelectedValues[0];
116+
}
117+
}
90118
}
91119

92120
#getCommonConfig(): Partial<TomSettings> {

src/Autocomplete/assets/test/controller.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,4 +726,53 @@ describe('AutocompleteController', () => {
726726
expect(fetchMock.requests()[0].url).toEqual('/path/to/autocomplete?query=');
727727
expect(fetchMock.requests()[1].url).toEqual('/path/to/autocomplete?query=foo');
728728
});
729+
730+
it('preserves the selected value and HTML with disconnect on single select', async () => {
731+
const { container, tomSelect } = await startAutocompleteTest(`
732+
<select data-testid="main-element" data-controller="autocomplete">
733+
<option value="">Select a dog</option>
734+
<option value="1">dog1</option>
735+
<option value="2">dog2</option>
736+
<option value="3">dog3</option>
737+
</select>
738+
`);
739+
740+
tomSelect.addItem('2');
741+
742+
const selectElement = getByTestId(container, 'main-element') as HTMLSelectElement;
743+
// trigger the disconnect
744+
selectElement.removeAttribute('data-controller');
745+
await waitFor(() => {
746+
expect(selectElement.className).not.toContain('tomselected');
747+
});
748+
expect(selectElement.value).toBe('2');
749+
});
750+
751+
it('preserves the selected value and HTML with disconnect on multiple select', async () => {
752+
const { container, tomSelect } = await startAutocompleteTest(`
753+
<select multiple data-testid="main-element" data-controller="autocomplete">
754+
<option value="">Select a dog</option>
755+
<option value="1">dog1</option>
756+
<option value="2">dog2</option>
757+
<option value="3">dog3</option>
758+
</select>
759+
`);
760+
761+
tomSelect.addItem('2');
762+
tomSelect.addItem('3');
763+
764+
const getSelectedValues = () => {
765+
return Array.from(selectElement.selectedOptions).map((option) => option.value).sort();
766+
}
767+
768+
const selectElement = getByTestId(container, 'main-element') as HTMLSelectElement;
769+
expect(getSelectedValues()).toEqual(['2', '3']);
770+
771+
// trigger the disconnect
772+
selectElement.removeAttribute('data-controller');
773+
await waitFor(() => {
774+
expect(selectElement.className).not.toContain('tomselected');
775+
});
776+
expect(getSelectedValues()).toEqual(['2', '3']);
777+
});
729778
});

0 commit comments

Comments
 (0)