Skip to content

Commit 7ee0373

Browse files
committed
feature #1219 [Autocomplete] Avoid destroying on disconnect (weaverryan)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Autocomplete] Avoid destroying on disconnect | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fix #1204 | License | MIT The tests pass - so I can't see a problem with NOT destroying the element. Commits ------- bcf6f2c [Autocomplete] Avoid destroying on disconnect
2 parents 64aad83 + bcf6f2c commit 7ee0373

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
@@ -61,7 +61,28 @@ class default_1 extends Controller {
6161
}
6262
disconnect() {
6363
this.stopMutationObserver();
64+
let currentSelectedValues = [];
65+
if (this.selectElement) {
66+
if (this.selectElement.multiple) {
67+
currentSelectedValues = Array.from(this.selectElement.options)
68+
.filter((option) => option.selected)
69+
.map((option) => option.value);
70+
}
71+
else {
72+
currentSelectedValues = [this.selectElement.value];
73+
}
74+
}
6475
this.tomSelect.destroy();
76+
if (this.selectElement) {
77+
if (this.selectElement.multiple) {
78+
Array.from(this.selectElement.options).forEach((option) => {
79+
option.selected = currentSelectedValues.includes(option.value);
80+
});
81+
}
82+
else {
83+
this.selectElement.value = currentSelectedValues[0];
84+
}
85+
}
6586
}
6687
getMaxOptions() {
6788
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
@@ -84,7 +84,35 @@ export default class extends Controller {
8484

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

90118
#getCommonConfig(): Partial<TomSettings> {

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,4 +766,53 @@ describe('AutocompleteController', () => {
766766
expect(fetchMock.requests()[0].url).toEqual('/path/to/autocomplete?query=');
767767
expect(fetchMock.requests()[1].url).toEqual('/path/to/autocomplete?query=foo');
768768
});
769+
770+
it('preserves the selected value and HTML with disconnect on single select', async () => {
771+
const { container, tomSelect } = await startAutocompleteTest(`
772+
<select data-testid="main-element" data-controller="autocomplete">
773+
<option value="">Select a dog</option>
774+
<option value="1">dog1</option>
775+
<option value="2">dog2</option>
776+
<option value="3">dog3</option>
777+
</select>
778+
`);
779+
780+
tomSelect.addItem('2');
781+
782+
const selectElement = getByTestId(container, 'main-element') as HTMLSelectElement;
783+
// trigger the disconnect
784+
selectElement.removeAttribute('data-controller');
785+
await waitFor(() => {
786+
expect(selectElement.className).not.toContain('tomselected');
787+
});
788+
expect(selectElement.value).toBe('2');
789+
});
790+
791+
it('preserves the selected value and HTML with disconnect on multiple select', async () => {
792+
const { container, tomSelect } = await startAutocompleteTest(`
793+
<select multiple data-testid="main-element" data-controller="autocomplete">
794+
<option value="">Select a dog</option>
795+
<option value="1">dog1</option>
796+
<option value="2">dog2</option>
797+
<option value="3">dog3</option>
798+
</select>
799+
`);
800+
801+
tomSelect.addItem('2');
802+
tomSelect.addItem('3');
803+
804+
const getSelectedValues = () => {
805+
return Array.from(selectElement.selectedOptions).map((option) => option.value).sort();
806+
}
807+
808+
const selectElement = getByTestId(container, 'main-element') as HTMLSelectElement;
809+
expect(getSelectedValues()).toEqual(['2', '3']);
810+
811+
// trigger the disconnect
812+
selectElement.removeAttribute('data-controller');
813+
await waitFor(() => {
814+
expect(selectElement.className).not.toContain('tomselected');
815+
});
816+
expect(getSelectedValues()).toEqual(['2', '3']);
817+
});
769818
});

0 commit comments

Comments
 (0)