Skip to content

Commit a336cda

Browse files
feature #492 [Autocomplete] add option to specify minimum characters for autocomplete (daFish)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Autocomplete] add option to specify minimum characters for autocomplete | Q | A | ------------- | --- | Bug fix? |no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Tickets | Fix #432 | License | MIT This change adds a new option `min_characters` for specifying the value of entered characters before the autocomplete is triggerd. This avoids calls with an empty query. Todos: - [x] add tests `@weaverryan` I also added a `CHANGELOG.md` because this file wasn't present. Commits ------- 604ad69 [Autocomplete] add option to specify minimum characters for autocomplete
2 parents b9ccfb1 + 00032d8 commit a336cda

File tree

7 files changed

+57
-4
lines changed

7 files changed

+57
-4
lines changed

src/Autocomplete/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHANGELOG
2+
3+
## 2.4.0
4+
5+
- Support added for setting the required minimum search query length (defaults to 3) (#492) - @daFish

src/Autocomplete/assets/dist/controller.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class default_1 extends Controller {
3939
}
4040
connect() {
4141
if (this.urlValue) {
42-
this.tomSelect = __classPrivateFieldGet(this, _instances, "m", _createAutocompleteWithRemoteData).call(this, this.urlValue);
42+
this.tomSelect = __classPrivateFieldGet(this, _instances, "m", _createAutocompleteWithRemoteData).call(this, this.urlValue, this.minCharactersValue);
4343
return;
4444
}
4545
if (this.optionsAsHtmlValue) {
@@ -121,7 +121,7 @@ _instances = new WeakSet(), _getCommonConfig = function _getCommonConfig() {
121121
},
122122
});
123123
return __classPrivateFieldGet(this, _instances, "m", _createTomSelect).call(this, config);
124-
}, _createAutocompleteWithRemoteData = function _createAutocompleteWithRemoteData(autocompleteEndpointUrl) {
124+
}, _createAutocompleteWithRemoteData = function _createAutocompleteWithRemoteData(autocompleteEndpointUrl, minCharacterLength) {
125125
const config = __classPrivateFieldGet(this, _instances, "m", _mergeObjects).call(this, __classPrivateFieldGet(this, _instances, "m", _getCommonConfig).call(this), {
126126
firstUrl: (query) => {
127127
const separator = autocompleteEndpointUrl.includes('?') ? '&' : '?';
@@ -134,6 +134,10 @@ _instances = new WeakSet(), _getCommonConfig = function _getCommonConfig() {
134134
.then(json => { this.setNextUrl(query, json.next_page); callback(json.results); })
135135
.catch(() => callback());
136136
},
137+
shouldLoad: function (query) {
138+
const minLength = minCharacterLength || 3;
139+
return query.length >= minLength;
140+
},
137141
score: function (search) {
138142
return function (item) {
139143
return 1;
@@ -173,6 +177,7 @@ default_1.values = {
173177
optionsAsHtml: Boolean,
174178
noResultsFoundText: String,
175179
noMoreResultsText: String,
180+
minCharacters: Number,
176181
tomSelectOptions: Object,
177182
};
178183

src/Autocomplete/assets/src/controller.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ export default class extends Controller {
88
optionsAsHtml: Boolean,
99
noResultsFoundText: String,
1010
noMoreResultsText: String,
11+
minCharacters: Number,
1112
tomSelectOptions: Object,
1213
}
1314

1415
readonly urlValue: string;
1516
readonly optionsAsHtmlValue: boolean;
1617
readonly noMoreResultsTextValue: string;
1718
readonly noResultsFoundTextValue: string;
19+
readonly minCharactersValue: number;
1820
readonly tomSelectOptionsValue: object;
1921
tomSelect: TomSelect;
2022

@@ -30,7 +32,7 @@ export default class extends Controller {
3032

3133
connect() {
3234
if (this.urlValue) {
33-
this.tomSelect = this.#createAutocompleteWithRemoteData(this.urlValue);
35+
this.tomSelect = this.#createAutocompleteWithRemoteData(this.urlValue, this.minCharactersValue);
3436

3537
return;
3638
}
@@ -124,7 +126,7 @@ export default class extends Controller {
124126
return this.#createTomSelect(config);
125127
}
126128

127-
#createAutocompleteWithRemoteData(autocompleteEndpointUrl: string): TomSelect {
129+
#createAutocompleteWithRemoteData(autocompleteEndpointUrl: string, minCharacterLength: number): TomSelect {
128130
const config: Partial<TomSettings> = this.#mergeObjects(this.#getCommonConfig(), {
129131
firstUrl: (query: string) => {
130132
const separator = autocompleteEndpointUrl.includes('?') ? '&' : '?';
@@ -142,6 +144,11 @@ export default class extends Controller {
142144
.then(json => { this.setNextUrl(query, json.next_page); callback(json.results) })
143145
.catch(() => callback());
144146
},
147+
shouldLoad: function (query: string) {
148+
const minLength = minCharacterLength || 3;
149+
150+
return query.length >= minLength;
151+
},
145152
// avoid extra filtering after results are returned
146153
score: function(search: string) {
147154
return function(item: any) {

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,35 @@ describe('AutocompleteController', () => {
138138
});
139139
});
140140

141+
it('limits updates when min-characters', async () => {
142+
const container = mountDOM(`
143+
<label for="the-select">Items</label>
144+
<select
145+
id="the-select"
146+
data-testid="main-element"
147+
data-controller="check autocomplete"
148+
data-autocomplete-url-value="/path/to/autocomplete"
149+
data-autocomplete-min-characters-value="3"
150+
></select>
151+
`);
152+
153+
application = startStimulus();
154+
155+
await waitFor(() => {
156+
expect(getByTestId(container, 'main-element')).toHaveClass('connected');
157+
});
158+
159+
const tomSelect = getByTestId(container, 'main-element').tomSelect;
160+
const controlInput = tomSelect.control_input;
161+
162+
controlInput.value = 'fo';
163+
controlInput.dispatchEvent(new Event('input'));
164+
165+
await waitFor(() => {
166+
expect(container.querySelectorAll('.option[data-selectable]')).toHaveLength(0);
167+
});
168+
});
169+
141170
it('adds live-component support', async () => {
142171
const container = mountDOM(`
143172
<div>

src/Autocomplete/src/Form/AutocompleteChoiceTypeExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public function finishView(FormView $view, FormInterface $form, array $options)
7171
$values['max-results'] = $options['max_results'];
7272
}
7373

74+
if ($options['min_characters']) {
75+
$values['min-characters'] = $options['min_characters'];
76+
}
77+
7478
$values['no-results-found-text'] = $this->trans($options['no_results_found_text']);
7579
$values['no-more-results-text'] = $this->trans($options['no_more_results_text']);
7680

@@ -91,6 +95,7 @@ public function configureOptions(OptionsResolver $resolver)
9195
'allow_options_create' => false,
9296
'no_results_found_text' => 'No results found',
9397
'no_more_results_text' => 'No more results',
98+
'min_characters' => 3,
9499
'max_results' => 10,
95100
]);
96101

src/Autocomplete/tests/Fixtures/Form/CategoryAutocompleteType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function configureOptions(OptionsResolver $resolver)
4242
'data-controller' => 'custom-autocomplete',
4343
],
4444
'max_results' => 5,
45+
'min_characters' => 2,
4546
]);
4647
}
4748

src/Autocomplete/tests/Functional/AutocompleteFormRenderingTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public function testFieldsRenderWithStimulusController()
3232
->get('/test-form')
3333
->assertElementAttributeContains('#product_category_autocomplete', 'data-controller', 'custom-autocomplete symfony--ux-autocomplete--autocomplete')
3434
->assertElementAttributeContains('#product_category_autocomplete', 'data-symfony--ux-autocomplete--autocomplete-url-value', '/test/autocomplete/category_autocomplete_type')
35+
->assertElementAttributeContains('#product_category_autocomplete', 'data-symfony--ux-autocomplete--autocomplete-min-characters-value', '2')
3536

3637
->assertElementAttributeContains('#product_portionSize', 'data-controller', 'symfony--ux-autocomplete--autocomplete')
3738
->assertElementAttributeContains('#product_tags', 'data-controller', 'symfony--ux-autocomplete--autocomplete')

0 commit comments

Comments
 (0)