(core) Filter menu show all options for Bool/Choice/Choice List columns

Summary:
> Toggle, Choice, and Choice List need all possible values available in filter, not just values present in current records
https://grist.quip.com/cjw4A8AHx1vh/Filtering-Improvements#temp:C:PZHc42e8be8cd8547bb8ce93fdb0

Test Plan: Adds new nbrowser test

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3436
This commit is contained in:
Cyprien P 2022-05-19 10:51:04 +02:00
parent 309ddb0fe7
commit 8f4f21e94a
3 changed files with 24 additions and 4 deletions

View File

@ -29,6 +29,10 @@ export class ColumnFilter extends Disposable {
this.setState(_initialFilterJson); this.setState(_initialFilterJson);
} }
public get columnType() {
return this._columnType;
}
public setState(filterJson: string|FilterSpec) { public setState(filterJson: string|FilterSpec) {
const state = makeFilterState(filterJson); const state = makeFilterState(filterJson);
this._include = state.include; this._include = state.include;

View File

@ -22,10 +22,10 @@ export class ColumnFilterMenuModel extends Disposable {
// computes a set of all keys that matches the search text. // computes a set of all keys that matches the search text.
public readonly filterSet = Computed.create(this, this.searchValue, (_use, searchValue) => { public readonly filterSet = Computed.create(this, this.searchValue, (_use, searchValue) => {
const searchRegex = new RegExp(escapeRegExp(searchValue), 'i'); const searchRegex = new RegExp(escapeRegExp(searchValue), 'i');
const showAllOptions = ['Bool', 'Choice', 'ChoiceList'].includes(this.columnFilter.columnType!);
return new Set( return new Set(
this._valueCount this._valueCount
.filter(([_, {count}]) => count) .filter(([_, {label, count}]) => (showAllOptions ? true : count) && searchRegex.test(label))
.filter(([_, {label}]) => searchRegex.test(label))
.map(([key]) => key) .map(([key]) => key)
); );
}); });

View File

@ -276,6 +276,23 @@ function formatUniqueCount(values: Array<[CellValue, IFilterCount]>) {
return count ? '(' + count.toLocaleString() + ')' : ''; return count ? '(' + count.toLocaleString() + ')' : '';
} }
/**
* Returns a new `Map` object to holds pairs of `CellValue` and `IFilterCount`. For `Bool`, `Choice`
* and `ChoiceList` type of column, the map is initialized with all possible values in order to make
* sure they get shown to the user.
*/
function getEmptyCountMap(fieldOrColumn: ViewFieldRec|ColumnRec): Map<CellValue, IFilterCount> {
const columnType = fieldOrColumn.origCol().type();
let values: any[] = [];
if (columnType === 'Bool') {
values = [true, false];
} else if (['Choice', 'ChoiceList'].includes(columnType)) {
const options = fieldOrColumn.origCol().widgetOptionsJson;
values = options.prop('choices')();
}
return new Map(values.map((v) => [v, {label: String(v), count: 0}]));
}
/** /**
* Returns content for the newly created columnFilterMenu; for use with setPopupToCreateDom(). * Returns content for the newly created columnFilterMenu; for use with setPopupToCreateDom().
*/ */
@ -297,14 +314,13 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio
sectionFilter.setFilterOverride(fieldOrColumn.getRowId(), columnFilter); // Will be removed on menu disposal sectionFilter.setFilterOverride(fieldOrColumn.getRowId(), columnFilter); // Will be removed on menu disposal
const [allRows, hiddenRows] = partition(Array.from(rowSource.getAllRows()), filterFunc.get()); const [allRows, hiddenRows] = partition(Array.from(rowSource.getAllRows()), filterFunc.get());
const valueCounts: Map<CellValue, {label: string, count: number}> = new Map(); const valueCounts = getEmptyCountMap(fieldOrColumn);
addCountsToMap(valueCounts, allRows, {keyMapFunc, labelMapFunc, columnType}); addCountsToMap(valueCounts, allRows, {keyMapFunc, labelMapFunc, columnType});
addCountsToMap(valueCounts, hiddenRows, {keyMapFunc, labelMapFunc, columnType, addCountsToMap(valueCounts, hiddenRows, {keyMapFunc, labelMapFunc, columnType,
areHiddenRows: true}); areHiddenRows: true});
const model = ColumnFilterMenuModel.create(openCtl, columnFilter, Array.from(valueCounts)); const model = ColumnFilterMenuModel.create(openCtl, columnFilter, Array.from(valueCounts));
return columnFilterMenu(openCtl, { return columnFilterMenu(openCtl, {
model, model,
valueCounts, valueCounts,