From bf809a61ffde34a8781bf1f5d3f9ed27b7d97127 Mon Sep 17 00:00:00 2001 From: Marco Falkenberg Date: Mon, 16 Dec 2024 13:56:41 +0100 Subject: [PATCH] fix(List): fix error when using defaultValues --- .../components/List/model/filter/Filter.ts | 69 +++++++++---------- .../List/model/filter/FilterValue.ts | 24 +++++-- .../src/components/List/model/filter/types.ts | 2 +- packages/components/src/lib/array/toArray.ts | 2 + 4 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 packages/components/src/lib/array/toArray.ts diff --git a/packages/components/src/components/List/model/filter/Filter.ts b/packages/components/src/components/List/model/filter/Filter.ts index 63de7e2fa..a74d8aa8c 100644 --- a/packages/components/src/components/List/model/filter/Filter.ts +++ b/packages/components/src/components/List/model/filter/Filter.ts @@ -19,20 +19,19 @@ import { customPropertyPrefix } from "@/components/List/model/types"; import { difference, unique } from "remeda"; import { FilterValue } from "@/components/List/model/filter/FilterValue"; import z from "zod"; +import { toArray } from "@/lib/array/toArray"; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const equalsPropertyMatcher: FilterMatcher = ( +const equalsPropertyMatcher: FilterMatcher = ( filterValue, propertyValue, ) => filterValue === propertyValue; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const stringCastRenderMethod: PropertyValueRenderMethod = (value) => +const stringCastRenderMethod: PropertyValueRenderMethod = (value) => String(value); export class Filter, TMatchValue> { public static readonly settingsStorageSchema = z - .record(z.array(z.unknown())) + .record(z.array(z.string())) .optional(); private _values?: FilterValue[] | undefined; @@ -44,31 +43,22 @@ export class Filter, TMatchValue> { public readonly renderItem: PropertyValueRenderMethod; public readonly name?: string; private onFilterUpdateCallbacks = new Set<() => unknown>(); - private readonly defaultSelectedValues?: FilterValue[]; + private readonly defaultSelectedValues?: readonly NonNullable[]; public constructor(list: List, shape: FilterShape) { this.list = list; this.property = shape.property; this.mode = shape.mode ?? "one"; - this._values = shape.values?.map((v) => new FilterValue(this, v)); + this._values = shape.values?.map((v) => FilterValue.create(this, v)); this.matcher = shape.matcher ?? equalsPropertyMatcher; this.renderItem = shape.renderItem ?? stringCastRenderMethod; this.name = shape.name; - this.defaultSelectedValues = shape.defaultSelected - ? this.values.filter((v) => - shape.defaultSelected?.some((d) => d === v.value), - ) - : undefined; + this.defaultSelectedValues = shape.defaultSelected; } - private getStoredDefaultSelectedValues() { - const storedValues = - this.list.getStoredFilterDefaultSettings()?.[String(this.property)]; - - return storedValues - ? this.values.filter((v) => storedValues.includes(v.id)) - : undefined; + private getStoredSelectedIds() { + return this.list.getStoredFilterDefaultSettings()?.[String(this.property)]; } public updateInitialState(initialState: InitialTableState) { @@ -104,25 +94,27 @@ export class Filter, TMatchValue> { private checkFilterMatches( property: unknown, - filterValue: FilterValue, + filterValueInput: unknown, ): boolean { - if (filterValue === null) { + if (filterValueInput === null) { return true; } - const toArray = (val: FilterValue | FilterValue[]): FilterValue[] => - Array.isArray(val) ? val : [val]; - const predicate = (filterValue: FilterValue) => this.matcher(filterValue.value as never, property as never); + const toFilterValue = (something: unknown) => + FilterValue.create(this, something); + if (this.mode === "all") { - return toArray(filterValue).every(predicate); + return toArray(filterValueInput).map(toFilterValue).every(predicate); } else if (this.mode === "some") { - const filterArr = toArray(filterValue); - return filterArr.length === 0 || filterArr.some(predicate); + const filterArr = toArray(filterValueInput); + return ( + filterArr.length === 0 || filterArr.map(toFilterValue).some(predicate) + ); } else if (this.mode === "one") { - return predicate(filterValue); + return predicate(toFilterValue(filterValueInput)); } throw new Error(`Unknown filter mode '${this.mode}'`); @@ -147,7 +139,7 @@ export class Filter, TMatchValue> { Array.from(this.getTableColumn().getFacetedUniqueValues().keys()) .flatMap((v) => v) .filter((v) => v !== undefined && v !== null), - ).map((v) => new FilterValue(this, v)); + ).map((v) => FilterValue.create(this, v)); } private checkIfValueIsUnknown(value: FilterValue) { @@ -179,12 +171,10 @@ export class Filter, TMatchValue> { } public getArrayValue(): FilterValue[] { - const currentValue = this.getValue(); - return Array.isArray(currentValue) - ? currentValue - : currentValue === null - ? [] - : [currentValue]; + const value = this.getValue(); + return value === null + ? [] + : toArray(value).map((v) => FilterValue.create(this, v)); } public isValueActive(value: FilterValue): boolean { @@ -214,7 +204,8 @@ export class Filter, TMatchValue> { public hasChanged(): boolean { const currentValues = this.getArrayValue().map((v) => v.value); - const initialValues = (this.getInitialValues() ?? []).map((v) => v.value); + const initialValues = + this.getInitialFilterValues()?.map((v) => v.value) ?? []; return ( currentValues.length !== initialValues.length || @@ -223,7 +214,11 @@ export class Filter, TMatchValue> { } private getInitialValues() { - return this.getStoredDefaultSelectedValues() ?? this.defaultSelectedValues; + return this.getStoredSelectedIds() ?? this.defaultSelectedValues; + } + + private getInitialFilterValues() { + return this.getInitialValues()?.map((v) => FilterValue.create(this, v)); } public resetValues(): void { diff --git a/packages/components/src/components/List/model/filter/FilterValue.ts b/packages/components/src/components/List/model/filter/FilterValue.ts index 3054fac0a..7f88bc23c 100644 --- a/packages/components/src/components/List/model/filter/FilterValue.ts +++ b/packages/components/src/components/List/model/filter/FilterValue.ts @@ -5,19 +5,31 @@ import { hash } from "object-code"; export class FilterValue { public readonly filter: Filter; public readonly value: unknown; + public readonly id: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - public constructor(filter: Filter, value: unknown) { + private constructor(filter: Filter, value: unknown) { this.filter = filter; - this.value = value; + + if (typeof value === "string" && value.startsWith("FilterValueId@@")) { + this.value = filter.values.find((v) => v.id === value)?.value; + this.id = value; + } else { + this.value = value; + this.id = `FilterValueId@@${this.filter.property}@@${hash(this.value)}`; + } } - public equals(otherValue: FilterValue) { - return isShallowEqual(this.value, otherValue.value); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public static create(filter: Filter, value: unknown) { + if (value instanceof FilterValue) { + return value; + } + return new FilterValue(filter, value); } - public get id() { - return `${this.filter.property}@@${hash(this.value)}`; + public equals(otherValue: FilterValue) { + return isShallowEqual(this.value, otherValue.value); } public get isActive() { diff --git a/packages/components/src/components/List/model/filter/types.ts b/packages/components/src/components/List/model/filter/types.ts index c47826f25..ee9b5feaa 100644 --- a/packages/components/src/components/List/model/filter/types.ts +++ b/packages/components/src/components/List/model/filter/types.ts @@ -19,5 +19,5 @@ export interface FilterShape, TMatcherValue> { matcher?: FilterMatcher; values?: readonly TMatcherValue[]; name?: string; - defaultSelected?: readonly NonNullable>[]; + defaultSelected?: readonly NonNullable[]; } diff --git a/packages/components/src/lib/array/toArray.ts b/packages/components/src/lib/array/toArray.ts new file mode 100644 index 000000000..d07470fac --- /dev/null +++ b/packages/components/src/lib/array/toArray.ts @@ -0,0 +1,2 @@ +export const toArray = (val: T | T[]): T[] => + Array.isArray(val) ? val : [val];