Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit c1fdde9

Browse files
authored
Merge pull request #731 from ghiscoding/feat/select-filter-empty-values
feat(filters): add option to filter empty values for select filter
2 parents 35b8eb9 + 7c9ce5a commit c1fdde9

File tree

6 files changed

+135
-65
lines changed

6 files changed

+135
-65
lines changed

src/app/modules/angular-slickgrid/filters/__tests__/multipleSelectFilter.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Column, FilterArguments, GridOption } from '../../models';
77
import { CollectionService } from '../../services/collection.service';
88
import { Filters } from '..';
99
import { MultipleSelectFilter } from '../multipleSelectFilter';
10-
import { of, Subject } from 'rxjs';
1110

1211
const containerId = 'demo-container';
1312

@@ -30,7 +29,7 @@ describe('MultipleSelectFilter', () => {
3029
let divContainer: HTMLDivElement;
3130
let filter: MultipleSelectFilter;
3231
let filterArguments: FilterArguments;
33-
let spyGetHeaderRow;
32+
let spyGetHeaderRow: any;
3433
let mockColumn: Column;
3534
let collectionService: CollectionService;
3635
let translate: TranslateService;
@@ -67,13 +66,14 @@ describe('MultipleSelectFilter', () => {
6766
});
6867

6968
it('should be a multiple-select filter', () => {
70-
mockColumn.filter.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
69+
mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
7170
filter = new MultipleSelectFilter(translate, collectionService);
72-
filter.init(filterArguments, true);
71+
filter.init(filterArguments);
7372
const filterCount = divContainer.querySelectorAll('select.ms-filter.search-filter.filter-gender').length;
7473

7574
expect(spyGetHeaderRow).toHaveBeenCalled();
7675
expect(filterCount).toBe(1);
7776
expect(filter.isMultipleSelect).toBe(true);
77+
expect(filter.columnDef.filter!.emptySearchTermReturnAllValues).toBeFalse();
7878
});
7979
});

src/app/modules/angular-slickgrid/filters/__tests__/singleSelectFilter.spec.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { Column, FilterArguments, GridOption } from '../../models';
77
import { CollectionService } from '../../services/collection.service';
88
import { Filters } from '..';
99
import { SingleSelectFilter } from '../singleSelectFilter';
10-
import { of, Subject } from 'rxjs';
1110

1211
const containerId = 'demo-container';
1312

@@ -30,7 +29,7 @@ describe('SingleSelectFilter', () => {
3029
let divContainer: HTMLDivElement;
3130
let filter: SingleSelectFilter;
3231
let filterArguments: FilterArguments;
33-
let spyGetHeaderRow;
32+
let spyGetHeaderRow: any;
3433
let mockColumn: Column;
3534
let collectionService: CollectionService;
3635
let translate: TranslateService;
@@ -81,21 +80,22 @@ describe('SingleSelectFilter', () => {
8180
});
8281

8382
it('should be a single-select filter', () => {
84-
mockColumn.filter.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
83+
mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
8584
filter = new SingleSelectFilter(translate, collectionService);
86-
filter.init(filterArguments, true);
85+
filter.init(filterArguments);
8786
const filterCount = divContainer.querySelectorAll('select.ms-filter.search-filter.filter-gender').length;
8887

8988
expect(spyGetHeaderRow).toHaveBeenCalled();
9089
expect(filterCount).toBe(1);
9190
expect(filter.isMultipleSelect).toBe(false);
91+
expect(filter.columnDef.filter!.emptySearchTermReturnAllValues).toBeUndefined();
9292
});
9393

9494
it('should create the select filter with empty search term when passed an empty string as a filter argument and not expect "filled" css class either', () => {
95-
mockColumn.filter.collection = [{ value: '', label: '' }, { value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
95+
mockColumn.filter!.collection = [{ value: '', label: '' }, { value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
9696

9797
filterArguments.searchTerms = [''];
98-
filter.init(filterArguments, true);
98+
filter.init(filterArguments);
9999
const filterListElm = divContainer.querySelectorAll<HTMLInputElement>(`[name=filter-gender].ms-drop ul>li input[type=radio]`);
100100

101101
const filterFilledElms = divContainer.querySelectorAll<HTMLDivElement>('.ms-parent.ms-filter.search-filter.filter-gender.filled');
@@ -105,10 +105,10 @@ describe('SingleSelectFilter', () => {
105105

106106
it('should trigger single select change event and expect the callback to be called when we select a single search term from dropdown list', () => {
107107
const spyCallback = jest.spyOn(filterArguments, 'callback');
108-
mockColumn.filter.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
108+
mockColumn.filter!.collection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
109109

110-
filter.init(filterArguments, true);
111-
const filterBtnElm = divContainer.querySelector<HTMLButtonElement>('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice');
110+
filter.init(filterArguments);
111+
const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement;
112112
const filterListElm = divContainer.querySelectorAll<HTMLInputElement>(`[name=filter-gender].ms-drop ul>li input[type=radio]`);
113113
filterBtnElm.click();
114114

@@ -134,10 +134,10 @@ describe('SingleSelectFilter', () => {
134134
};
135135

136136
filterArguments.searchTerms = ['male', 'female'];
137-
filter.init(filterArguments, true);
137+
filter.init(filterArguments);
138138

139139
setTimeout(() => {
140-
const filterBtnElm = divContainer.querySelector<HTMLButtonElement>('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice');
140+
const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement;
141141
const filterListElm = divContainer.querySelectorAll<HTMLSpanElement>(`[name=filter-gender].ms-drop ul>li span`);
142142
const filterOkElm = divContainer.querySelectorAll<HTMLButtonElement>(`[name=filter-gender].ms-drop .ms-ok-button`);
143143
const filterSelectAllElm = divContainer.querySelectorAll<HTMLSpanElement>('.filter-gender .ms-select-all label span');
@@ -167,9 +167,9 @@ describe('SingleSelectFilter', () => {
167167
};
168168

169169
filterArguments.searchTerms = ['male', 'female'];
170-
filter.init(filterArguments, true);
170+
filter.init(filterArguments);
171171
setTimeout(() => {
172-
const filterBtnElm = divContainer.querySelector<HTMLButtonElement>('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice');
172+
const filterBtnElm = divContainer.querySelector('.ms-parent.ms-filter.search-filter.filter-gender button.ms-choice') as HTMLButtonElement;
173173
const filterListElm = divContainer.querySelectorAll<HTMLSpanElement>(`[name=filter-gender].ms-drop ul>li span`);
174174
filterBtnElm.click();
175175

src/app/modules/angular-slickgrid/filters/selectFilter.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ export class SelectFilter implements Filter {
149149
}
150150
this.defaultOptions.placeholder = placeholder || '';
151151

152+
// when we're using a multiple-select filter and we have an empty select option,
153+
// we probably want this value to be a valid filter option that will ONLY return value that are empty (not everything like its default behavior)
154+
// user can still override it by defining it
155+
if (this._isMultipleSelect && this.columnDef?.filter) {
156+
this.columnDef.filter.emptySearchTermReturnAllValues = this.columnDef.filter?.emptySearchTermReturnAllValues ?? false;
157+
}
158+
152159
// always render the Select (dropdown) DOM element, even if user passed a "collectionAsync",
153160
// if that is the case, the Select will simply be without any options but we still have to render it (else SlickGrid would throw an error)
154161
const newCollection = this.columnFilter.collection || [];

src/app/modules/angular-slickgrid/models/columnFilter.interface.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ export interface ColumnFilter {
111111
*/
112112
queryField?: string;
113113

114+
/**
115+
* Defaults to true, should an empty search term have the effect of returning all results?
116+
* Typically that would be True except for a dropdown Select Filter,
117+
* we might really want to filter an empty string and/or `undefined` and for these special cases we can set this flag to `false`.
118+
*
119+
* NOTE: for a dropdown Select Filter, we will assume that on a multipleSelect Filter it should default to `false`
120+
* however for a singleSelect Filter (and any other type of Filters) it should default to `true`.
121+
* In any case, the user can overrides it this flag.
122+
*/
123+
emptySearchTermReturnAllValues?: boolean;
124+
114125
/** What is the Field Type that can be used by the Filter (as precedence over the "type" set the column definition) */
115126
type?: FieldType;
116127

0 commit comments

Comments
 (0)