Skip to content

Commit f8ed736

Browse files
committed
feature #1505 [Autocomplete] Reset TomSelect when updating controller attributes (pierredup)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Autocomplete] Reset TomSelect when updating controller attributes | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Issues | Fix #1500 | License | MIT When using remote data for Autocomplete, the mutation observer is not started, so the select cannot be reset when some options change (E.G when limiting the options returned after selecting other options using extra_options from #1322). Instead, we start the mutation observer when using remote data. When the observer runs and checks if the options have changes, we explicitly set the `areOptionsEquivalent` to `true`, since the page will just render an empty `select`. We also check if any of the `data-autocomplete` attributes changes and forces a reset (E.G using extra_options, the `data-autocomplete-url` value can be different when using extra options, so we reset the select when this attribute changes). This fixes only one of the issues from #1500, I'll investigate the other issues and create separate PRs. Commits ------- d8fb3fb [Autocomplete] Reset TomSelect when updating controller attributes
2 parents cddafa8 + d8fb3fb commit f8ed736

File tree

5 files changed

+86
-3
lines changed

5 files changed

+86
-3
lines changed

Diff for: src/Autocomplete/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 2.23.0
4+
5+
- Reset TomSelect when updating url attribute #1505
6+
37
## 2.22.0
48

59
- Take `labelField` TomSelect option into account #2382

Diff for: src/Autocomplete/assets/dist/controller.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default class extends Controller {
4040
connect(): void;
4141
initializeTomSelect(): void;
4242
disconnect(): void;
43+
urlValueChanged(): void;
4344
private getMaxOptions;
4445
get selectElement(): HTMLSelectElement | null;
4546
get formElement(): HTMLInputElement | HTMLSelectElement;

Diff for: src/Autocomplete/assets/dist/controller.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class default_1 extends Controller {
9191
}
9292
}
9393
}
94+
urlValueChanged() {
95+
this.resetTomSelect();
96+
}
9497
getMaxOptions() {
9598
return this.selectElement ? this.selectElement.options.length : 50;
9699
}
@@ -131,7 +134,6 @@ class default_1 extends Controller {
131134
this.element.innerHTML = currentHtml;
132135
this.initializeTomSelect();
133136
this.tomSelect.setValue(currentValue);
134-
this.startMutationObserver();
135137
}
136138
}
137139
changeTomSelectDisabledState(isDisabled) {

Diff for: src/Autocomplete/assets/src/controller.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ export default class extends Controller {
128128
}
129129
}
130130

131+
urlValueChanged() {
132+
this.resetTomSelect();
133+
}
134+
131135
#getCommonConfig(): Partial<TomSettings> {
132136
const plugins: TPluginHash = {};
133137

@@ -391,8 +395,6 @@ export default class extends Controller {
391395
this.element.innerHTML = currentHtml;
392396
this.initializeTomSelect();
393397
this.tomSelect.setValue(currentValue);
394-
395-
this.startMutationObserver();
396398
}
397399
}
398400

Diff for: src/Autocomplete/assets/test/controller.test.ts

+74
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,80 @@ describe('AutocompleteController', () => {
138138
expect(fetchMock.requests()[1].url).toEqual('/path/to/autocomplete?query=foo');
139139
});
140140

141+
it('resets when ajax URL attribute on a select element changes', async () => {
142+
const { container, tomSelect } = await startAutocompleteTest(`
143+
<label for="the-select">Items</label>
144+
<select
145+
id="the-select"
146+
data-testid="main-element"
147+
data-controller="autocomplete"
148+
data-autocomplete-url-value="/path/to/autocomplete"
149+
></select>
150+
`);
151+
152+
const selectElement = getByTestId(container, 'main-element') as HTMLSelectElement;
153+
154+
// initial Ajax request on focus
155+
fetchMock.mockResponseOnce(
156+
JSON.stringify({
157+
results: [
158+
{
159+
value: 3,
160+
text: 'salad',
161+
},
162+
],
163+
})
164+
);
165+
166+
fetchMock.mockResponseOnce(
167+
JSON.stringify({
168+
results: [
169+
{
170+
value: 1,
171+
text: 'pizza',
172+
},
173+
{
174+
value: 2,
175+
text: 'popcorn',
176+
},
177+
],
178+
})
179+
);
180+
181+
const controlInput = tomSelect.control_input;
182+
183+
// wait for the initial Ajax request to finish
184+
userEvent.click(controlInput);
185+
await waitFor(() => {
186+
expect(container.querySelectorAll('.option[data-selectable]')).toHaveLength(1);
187+
});
188+
189+
controlInput.value = 'foo';
190+
controlInput.dispatchEvent(new Event('input'));
191+
192+
await waitFor(() => {
193+
expect(container.querySelectorAll('.option[data-selectable]')).toHaveLength(2);
194+
});
195+
196+
expect(selectElement.value).toBe('');
197+
tomSelect.addItem('2');
198+
expect(selectElement.value).toBe('2');
199+
200+
selectElement.outerHTML = `
201+
<select
202+
id="the-select"
203+
data-testid="main-element"
204+
data-controller="autocomplete"
205+
data-autocomplete-url-value="/path/to/autocomplete2"
206+
></select>
207+
`;
208+
209+
// wait for the MutationObserver to flush these changes
210+
await shortDelay(10);
211+
212+
expect(selectElement.value).toBe('');
213+
});
214+
141215
it('connect with ajax URL on an input element', async () => {
142216
const { container, tomSelect } = await startAutocompleteTest(`
143217
<label for="the-input">Items</label>

0 commit comments

Comments
 (0)