From 8f4f21e94a18472671bf2a59e164425a3830f543 Mon Sep 17 00:00:00 2001 From: Cyprien P Date: Thu, 19 May 2022 10:51:04 +0200 Subject: [PATCH] (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 --- app/client/models/ColumnFilter.ts | 4 ++++ app/client/models/ColumnFilterMenuModel.ts | 4 ++-- app/client/ui/ColumnFilterMenu.ts | 20 ++++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/client/models/ColumnFilter.ts b/app/client/models/ColumnFilter.ts index 4a890cb6..73217381 100644 --- a/app/client/models/ColumnFilter.ts +++ b/app/client/models/ColumnFilter.ts @@ -29,6 +29,10 @@ export class ColumnFilter extends Disposable { this.setState(_initialFilterJson); } + public get columnType() { + return this._columnType; + } + public setState(filterJson: string|FilterSpec) { const state = makeFilterState(filterJson); this._include = state.include; diff --git a/app/client/models/ColumnFilterMenuModel.ts b/app/client/models/ColumnFilterMenuModel.ts index d9d6c021..bf46d6a7 100644 --- a/app/client/models/ColumnFilterMenuModel.ts +++ b/app/client/models/ColumnFilterMenuModel.ts @@ -22,10 +22,10 @@ export class ColumnFilterMenuModel extends Disposable { // computes a set of all keys that matches the search text. public readonly filterSet = Computed.create(this, this.searchValue, (_use, searchValue) => { const searchRegex = new RegExp(escapeRegExp(searchValue), 'i'); + const showAllOptions = ['Bool', 'Choice', 'ChoiceList'].includes(this.columnFilter.columnType!); return new Set( this._valueCount - .filter(([_, {count}]) => count) - .filter(([_, {label}]) => searchRegex.test(label)) + .filter(([_, {label, count}]) => (showAllOptions ? true : count) && searchRegex.test(label)) .map(([key]) => key) ); }); diff --git a/app/client/ui/ColumnFilterMenu.ts b/app/client/ui/ColumnFilterMenu.ts index 4ca761d5..d39ff029 100644 --- a/app/client/ui/ColumnFilterMenu.ts +++ b/app/client/ui/ColumnFilterMenu.ts @@ -276,6 +276,23 @@ function formatUniqueCount(values: Array<[CellValue, IFilterCount]>) { 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 { + 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(). */ @@ -297,14 +314,13 @@ export function createFilterMenu(openCtl: IOpenController, sectionFilter: Sectio sectionFilter.setFilterOverride(fieldOrColumn.getRowId(), columnFilter); // Will be removed on menu disposal const [allRows, hiddenRows] = partition(Array.from(rowSource.getAllRows()), filterFunc.get()); - const valueCounts: Map = new Map(); + const valueCounts = getEmptyCountMap(fieldOrColumn); addCountsToMap(valueCounts, allRows, {keyMapFunc, labelMapFunc, columnType}); addCountsToMap(valueCounts, hiddenRows, {keyMapFunc, labelMapFunc, columnType, areHiddenRows: true}); const model = ColumnFilterMenuModel.create(openCtl, columnFilter, Array.from(valueCounts)); - return columnFilterMenu(openCtl, { model, valueCounts,