Skip to content

Commit c8945c2

Browse files
feat: add custom filtering functionality to combobox component
1 parent 2696526 commit c8945c2

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

packages/components/src/components/combobox/combobox.e2e.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,26 @@ describe('scale-combobox', () => {
209209
// Verify no scaleChange event was emitted
210210
expect(scaleChangeEvent).not.toHaveReceivedEvent();
211211
});
212+
213+
it('respects custom filter function', async () => {
214+
const page = await newE2EPage();
215+
const html = `
216+
<scale-combobox></scale-combobox>
217+
<script>
218+
const combobox = document.querySelector('scale-combobox');
219+
combobox.options = ['test', 'estt', 'stte'];
220+
combobox.filterFunction = (option, query) =>
221+
option.toLowerCase().startsWith(query.toLowerCase());
222+
</script>
223+
`;
224+
await page.setContent(html);
225+
const input = await page.find('scale-combobox >>> .combobox-input');
226+
await input.focus();
227+
await input.type('st');
228+
await page.waitForTimeout(100);
229+
const options = await page.findAll('scale-combobox >>> .combobox-option');
230+
// Should only show React due to custom filter function
231+
expect(options.length).toBeLessThanOrEqual(1);
232+
expect(options[0]).toEqualText('stte');
233+
});
212234
});

packages/components/src/components/combobox/combobox.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class Combobox {
3838
@Prop() options: string[] = [];
3939

4040
/** Current selected value */
41-
@Prop({mutable: true}) value?: string = '';
41+
@Prop({ mutable: true }) value?: string = '';
4242

4343
/** Whether the combobox is disabled */
4444
@Prop() disabled?: boolean = false;
@@ -52,6 +52,28 @@ export class Combobox {
5252
/** Invalid state */
5353
@Prop() invalid?: boolean = false;
5454

55+
/** Custom filtering function */
56+
@Prop() filterFunction?: (option: string, query: string) => boolean;
57+
58+
@Watch('filterFunction')
59+
validateFilterFunction(customFilterFn: any) {
60+
if (!customFilterFn) return false;
61+
62+
if (typeof customFilterFn !== 'function') {
63+
throw new Error(
64+
'scale-combobox: The provided filterFunction prop is not a valid function. Falling back to default filtering behavior.'
65+
);
66+
}
67+
68+
// Check the return type by executing the funciton with simple paylaod
69+
const testResult = customFilterFn('test option', 'test query');
70+
if (typeof testResult !== 'boolean') {
71+
throw new Error(
72+
'scale-combobox: The provided filterFunction prop does not return a boolean value. Falling back to default filtering behavior.'
73+
);
74+
}
75+
}
76+
5577
@State() isOpen = false;
5678
@State() filteredOptions: string[] = [];
5779
@State() highlightedIndex = -1;
@@ -220,11 +242,13 @@ export class Combobox {
220242

221243
private filterOptions(query: string) {
222244
const filtered = this.options.filter((option) =>
223-
option.toLowerCase().includes(query.toLowerCase())
245+
this.filterFunction
246+
? this.filterFunction(option, query)
247+
: option.toLowerCase().includes(query.toLowerCase())
224248
);
225249
this.filteredOptions = filtered;
226250
this.highlightedIndex = -1;
227-
251+
228252
// Update listbox position when filtered options change (dropdown size changes)
229253
if (this.isOpen) {
230254
setTimeout(() => {

packages/components/src/html/combobox.html

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ <h2>Invalid Combobox</h2>
111111
</div>
112112
</div>
113113

114+
<!-- Custom Filtering Example -->
115+
<div class="example">
116+
<h2>Combobox with Custom Filtering</h2>
117+
<p class="description">
118+
Implement custom filtering logic to control which options are displayed.
119+
</p>
120+
<scale-combobox
121+
id="custom-filter-combobox"
122+
label="Select a framework"
123+
placeholder="Type or select..."
124+
></scale-combobox>
125+
</div>
126+
114127
<script>
115128
// Framework options
116129
const frameworks = [
@@ -160,6 +173,18 @@ <h2>Invalid Combobox</h2>
160173
// Disabled combobox setup
161174
const disabledCombobox = document.getElementById('disabled-combobox');
162175
disabledCombobox.options = ['Option 1', 'Option 2', 'Option 3'];
176+
177+
// Custom filter combobox setup
178+
const customFilterCombobox = document.getElementById(
179+
'custom-filter-combobox'
180+
);
181+
customFilterCombobox.options = frameworks;
182+
customFilterCombobox.filterFunction = (option, query) =>
183+
option.toLowerCase().startsWith(query.toLowerCase());
184+
185+
customFilterCombobox.addEventListener('scaleChange', (e) => {
186+
console.log('Custom filter combobox changed:', e.detail.value);
187+
});
163188
</script>
164189
</body>
165190
</html>

0 commit comments

Comments
 (0)