Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed the "Is blank"/"Is not blank" filters for the tags field type #3637

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@

* @ag-grid `30.x -> 31.x`

### 🐞 Bug Fixes

* Fixed several issues with the grid column filter when used on a "tags" field.

## 63.0.3 - 2024-04-16

### 🐞 Bug Fixes
Expand Down
17 changes: 12 additions & 5 deletions cmp/grid/filter/GridFilterFieldSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
BaseFilterFieldSpec,
BaseFilterFieldSpecConfig
} from '@xh/hoist/data/filter/BaseFilterFieldSpec';
import {castArray, compact, flatten, isDate, isEmpty, uniqBy} from 'lodash';
import {castArray, compact, flatten, isDate, isEmpty, isEqual, uniqBy} from 'lodash';
import {GridFilterModel} from './GridFilterModel';

export interface GridFilterFieldSpecConfig extends BaseFilterFieldSpecConfig {
Expand Down Expand Up @@ -82,10 +82,17 @@ export class GridFilterFieldSpec extends BaseFilterFieldSpec {
// Get values from current column filter
const filterValues = [];
columnFilters.forEach(filter => {
const newValues = castArray(filter.value).map(value => {
value = sourceField.parseVal(value);
return filterModel.toDisplayValue(value);
});
// The parseVal of tag will castArray the value a second time, so make sure to flatten at the end.
const newValues = flatten(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing, but I think this would be more readable if we flatten() on line 96 as we push to filterValues

castArray(filter.value)
.map(value => {
value = sourceField.parseVal(value);
return filterModel.toDisplayValue(value);
})
// Filter out the null tag value as it isn't a valid value to display in value lists.
// It is set by 'is blank'/'is not blank' filters.
.filter(it => isEqual(it, [null]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems safe enough, independent of whether we continue to put null in as filter values.

);
filterValues.push(...newValues);
});

Expand Down
18 changes: 10 additions & 8 deletions data/filter/FieldFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
escapeRegExp,
first,
isArray,
isEmpty,
isEqual,
isNil,
isString,
Expand Down Expand Up @@ -114,10 +115,11 @@ export class FieldFilter extends Filter {
const storeField = store.getField(field);
if (!storeField) return () => true; // Ignore (do not filter out) if field not in store

const fieldType = storeField.type === 'tags' ? 'string' : storeField.type;
value = isArray(value)
? value.map(v => parseFieldValue(v, fieldType))
: parseFieldValue(value, fieldType);
const fieldType = storeField.type;
value =
isArray(value) && storeField.type !== 'tags'
? value.map(v => parseFieldValue(v, fieldType))
: parseFieldValue(value, fieldType);
}

if (FieldFilter.ARRAY_OPERATORS.includes(op)) {
Expand All @@ -128,14 +130,14 @@ export class FieldFilter extends Filter {
switch (op) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would rValue and fValue be more helpful here? Very hard to keep track of which is which.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion! Might I suggest we are even more explicit and call them recordVal and filterVal (i.e. more characters for the source than the word 'value')

case '=':
opFn = v => {
if (isNil(v) || v === '') v = null;
return value.some(it => isEqual(v, it));
if (isNil(v) || v === '' || (isArray(v) && isEmpty(v))) v = null;
return (v == null && isEmpty(value)) || value.some(it => isEqual(v, it));
};
break;
case '!=':
opFn = v => {
if (isNil(v) || v === '') v = null;
return !value.some(it => isEqual(v, it));
if (isNil(v) || v === '' || (isArray(v) && isEmpty(v))) v = null;
return (v != null || !isEmpty(value)) && !value.some(it => isEqual(v, it));
};
break;
case '>':
Expand Down
2 changes: 1 addition & 1 deletion desktop/cmp/grid/impl/filter/ColumnHeaderFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,6 @@ const switcherButton = hoistCmp.factory<ColumnHeaderFilterModel>(({model, id, ti
text: title,
active: activeTabId === id,
outlined: true,
onClick: () => tabContainerModel.activateTab(id)
onClick: () => model.activateTab(id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabContainerModel reference no longer needed on line 114

});
});
15 changes: 13 additions & 2 deletions desktop/cmp/grid/impl/filter/ColumnHeaderFilterModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,15 @@ export class ColumnHeaderFilterModel extends HoistModel {

@computed
get isCustomFilter() {
const {columnCompoundFilter, columnFilters} = this;
const {columnCompoundFilter, columnFilters, fieldType} = this;
if (columnCompoundFilter) return true;
if (isEmpty(columnFilters)) return false;
return columnFilters.some(it => !['=', '!=', 'includes'].includes(it.op));
return columnFilters.some(
it =>
!['=', '!=', 'includes'].includes(it.op) ||
Copy link
Member

@lbwexler lbwexler Jul 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code (line 75 and 77) would be easier to grok if we expanded out to equals checks, and did not try to use the javascript 'includes' function . We are already dealing with an 'includes' filter and it is confusing!

// The is blank / is not blank filter on tags is only supported by custom filter.
(fieldType === 'tags' && ['=', '!='].includes(it.op) && it.value == null)
);
}

get commitOnChange() {
Expand Down Expand Up @@ -158,6 +163,12 @@ export class ColumnHeaderFilterModel extends HoistModel {
this.isOpen = false;
}

activateTab(tabId) {
const tabModel = tabId === 'valuesFilter' ? this.valuesTabModel : this.customTabModel;
tabModel.syncWithFilter();
this.tabContainerModel.activateTab(tabId);
}

//-------------------
// Implementation
//-------------------
Expand Down
4 changes: 2 additions & 2 deletions desktop/cmp/grid/impl/filter/custom/CustomRowModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {HoistModel} from '@xh/hoist/core';
import {FieldFilterOperator, FieldFilterSpec} from '@xh/hoist/data';
import {ColumnHeaderFilterModel} from '@xh/hoist/desktop/cmp/grid/impl/filter/ColumnHeaderFilterModel';
import {bindable, computed, makeObservable} from '@xh/hoist/mobx';
import {isArray, isNil} from 'lodash';
import {isArray, isEmpty, isNil} from 'lodash';
import {CustomTabModel} from './CustomTabModel';

type OperatorOptionValue = 'blank' | 'not blank' | FieldFilterOperator;
Expand Down Expand Up @@ -82,7 +82,7 @@ export class CustomRowModel extends HoistModel {
makeObservable(this);

let newOp = op as OperatorOptionValue;
if (isNil(value)) {
if (isNil(value) || (isArray(value) && isEmpty(value))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might add a util isEmptyArray() -- we do this check 4 times in this PR -- might be others in hoist

if (op === '=') newOp = 'blank';
if (op === '!=') newOp = 'not blank';
}
Expand Down
17 changes: 12 additions & 5 deletions desktop/cmp/grid/impl/filter/values/ValuesTabModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {FieldFilterSpec} from '@xh/hoist/data';
import {ColumnHeaderFilterModel} from '../ColumnHeaderFilterModel';
import {checkbox} from '@xh/hoist/desktop/cmp/input';
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
import {castArray, difference, isEmpty, partition, uniq, without} from 'lodash';
import {castArray, difference, flatten, isEmpty, isEqual, partition, uniq, without} from 'lodash';

export class ValuesTabModel extends HoistModel {
override xhImpl = true;
Expand Down Expand Up @@ -162,10 +162,17 @@ export class ValuesTabModel extends HoistModel {
filterValues = [];

arr.forEach(filter => {
const newValues = castArray(filter.value).map(value => {
value = fieldSpec.sourceField.parseVal(value);
return gridFilterModel.toDisplayValue(value);
});
// The parseVal of tag will castArray the value a second time, so make sure to flatten at the end.
const newValues = flatten(
castArray(filter.value)
.map(value => {
value = fieldSpec.sourceField.parseVal(value);
return gridFilterModel.toDisplayValue(value);
})
// Filter out the null tag value as it isn't a valid value to display in value lists.
// It is set by 'is blank'/'is not blank' filters.
.filter(it => isEqual(it, [null]))
);
filterValues.push(...newValues); // Todo: Is this safe?
});

Expand Down
Loading