Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@
},
"targetDefaults": {
"test:lib": {
"dependsOn": ["^build"],
"dependsOn": ["^build", "build"],
"inputs": ["default", "^public"],
"outputs": ["{projectRoot}/coverage"],
"cache": true
},
"test:eslint": {
"dependsOn": ["^build"],
"dependsOn": ["^build", "build"],
"inputs": ["default", "^public"],
"cache": true
},
"test:types": {
"dependsOn": ["^build"],
"dependsOn": ["^build", "build"],
"inputs": ["default", "^public"],
"cache": true
},
"test:e2e": {
"dependsOn": ["^build"],
"dependsOn": ["^build", "build"],
"inputs": ["default", "^public"],
"cache": true
},
Expand All @@ -55,7 +55,7 @@
"cache": true
},
"build": {
"dependsOn": ["^build"],
"dependsOn": ["^build", { "target": "format", "projects": "table" }],
"inputs": ["default", "^public"],
"outputs": ["{projectRoot}/build", "{projectRoot}/dist"],
"cache": true
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
},
"nx": {
"includedScripts": [
"format",
"test:docs",
"test:knip",
"test:sherif"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ export interface CachedRowModel_Faceted<
in out TFeatures extends TableFeatures,
in out TData extends RowData,
> {
facetedRowModel?: (columnId: string) => () => RowModel<TFeatures, TData>
facetedMinMaxValues?: (columnId: string) => [number, number]
facetedUniqueValues?: (columnId: string) => Map<any, number>
facetedRowModels?: Record<string, () => RowModel<TFeatures, TData>>
facetedMinMaxValues?: Record<string, () => undefined | [number, number]>
facetedUniqueValues?: Record<string, () => Map<any, number>>
globalFacetedRowModel?: () => RowModel<TFeatures, TData>
globalFacetedMinMaxValues?: () => undefined | [number, number]
globalFacetedUniqueValues?: () => Map<any, number>
}

export interface Table_ColumnFaceting<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { makeObjectMap } from '../../utils'
import type { CellData, RowData } from '../../types/type-utils'
import type { TableFeatures } from '../../types/TableFeatures'
import type { RowModel } from '../../core/row-models/coreRowModelsFeature.types'
Expand All @@ -23,9 +24,16 @@ export function column_getFacetedMinMaxValues<
column: Column_Internal<TFeatures, TData, TValue>,
table: Table_Internal<TFeatures, TData>,
): [number, number] | undefined {
const facetedMinMaxValuesFn =
table.options.features.facetedMinMaxValues?.(table, column.id) ??
(() => undefined)
const facetedMinMaxValues = (table._rowModels.facetedMinMaxValues ??=
makeObjectMap())
let facetedMinMaxValuesFn = facetedMinMaxValues[column.id]

if (!facetedMinMaxValuesFn) {
facetedMinMaxValuesFn = facetedMinMaxValues[column.id] =
table.options.features.facetedMinMaxValues?.(table, column.id) ??
(() => undefined)
}

return facetedMinMaxValuesFn()
}

Expand All @@ -49,9 +57,17 @@ export function column_getFacetedRowModel<
column: Column_Internal<TFeatures, TData, TValue> | undefined,
table: Table_Internal<TFeatures, TData>,
): RowModel<TFeatures, TData> {
const facetedRowModelFn =
table.options.features.facetedRowModel?.(table, column?.id ?? '') ??
(() => table.getPreFilteredRowModel())
const columnId = column?.id ?? ''
const facetedRowModels = (table._rowModels.facetedRowModels ??=
makeObjectMap())
let facetedRowModelFn = facetedRowModels[columnId]

if (!facetedRowModelFn) {
facetedRowModelFn = facetedRowModels[columnId] =
table.options.features.facetedRowModel?.(table, columnId) ??
(() => table.getPreFilteredRowModel())
}

return facetedRowModelFn()
}

Expand All @@ -74,9 +90,16 @@ export function column_getFacetedUniqueValues<
column: Column_Internal<TFeatures, TData, TValue>,
table: Table_Internal<TFeatures, TData>,
): Map<any, number> {
const facetedUniqueValuesFn =
table.options.features.facetedUniqueValues?.(table, column.id) ??
(() => new Map<any, number>())
const facetedUniqueValues = (table._rowModels.facetedUniqueValues ??=
makeObjectMap())
let facetedUniqueValuesFn = facetedUniqueValues[column.id]

if (!facetedUniqueValuesFn) {
facetedUniqueValuesFn = facetedUniqueValues[column.id] =
table.options.features.facetedUniqueValues?.(table, column.id) ??
(() => new Map<any, number>())
}

return facetedUniqueValuesFn()
}

Expand All @@ -95,9 +118,13 @@ export function table_getGlobalFacetedMinMaxValues<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): undefined | [number, number] {
const facetedMinMaxValuesFn =
table.options.features.facetedMinMaxValues?.(table, '__global__') ??
(() => undefined)
if (!table._rowModels.globalFacetedMinMaxValues) {
table._rowModels.globalFacetedMinMaxValues =
table.options.features.facetedMinMaxValues?.(table, '__global__') ??
(() => undefined)
}

const facetedMinMaxValuesFn = table._rowModels.globalFacetedMinMaxValues
return facetedMinMaxValuesFn()
}

Expand All @@ -117,9 +144,13 @@ export function table_getGlobalFacetedRowModel<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): RowModel<TFeatures, TData> {
const facetedRowModelFn =
table.options.features.facetedRowModel?.(table, '__global__') ??
(() => table.getPreFilteredRowModel())
if (!table._rowModels.globalFacetedRowModel) {
table._rowModels.globalFacetedRowModel =
table.options.features.facetedRowModel?.(table, '__global__') ??
(() => table.getPreFilteredRowModel())
}

const facetedRowModelFn = table._rowModels.globalFacetedRowModel
return facetedRowModelFn()
}

Expand All @@ -138,8 +169,12 @@ export function table_getGlobalFacetedUniqueValues<
TFeatures extends TableFeatures,
TData extends RowData,
>(table: Table_Internal<TFeatures, TData>): Map<any, number> {
const facetedUniqueValuesFn =
table.options.features.facetedUniqueValues?.(table, '__global__') ??
(() => new Map())
if (!table._rowModels.globalFacetedUniqueValues) {
table._rowModels.globalFacetedUniqueValues =
table.options.features.facetedUniqueValues?.(table, '__global__') ??
(() => new Map())
}

const facetedUniqueValuesFn = table._rowModels.globalFacetedUniqueValues
return facetedUniqueValuesFn()
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ function _createFacetedRowModel<
}
if (globalFilter) filterableIds.push('__global__')

if (!filterableIds.length) {
return preRowModel
}

const filterRowsImpl = (
row: Row<TFeatures, TData> & Partial<Row_ColumnFiltering<TFeatures, TData>>,
) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,11 @@ function _createFacetedUniqueValues<

for (let j = 0; j < values.length; j++) {
const value = values[j]
if (facetedUniqueValues.has(value)) {
facetedUniqueValues.set(
value,
(facetedUniqueValues.get(value) ?? 0) + 1,
)
} else {
facetedUniqueValues.set(value, 1)
}
const previousValue = facetedUniqueValues.get(value)
facetedUniqueValues.set(
value,
previousValue === undefined ? 1 : previousValue + 1,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { describe, expect, it, vi } from 'vitest'
import {
columnFacetingFeature,
columnFilteringFeature,
constructTable,
coreFeatures,
createFacetedMinMaxValues,
createFacetedRowModel,
createFacetedUniqueValues,
createFilteredRowModel,
filterFns,
} from '../../../../src'
import { storeReactivityBindings } from '../../../../src/store-reactivity-bindings'
import {
column_getFacetedMinMaxValues,
column_getFacetedRowModel,
column_getFacetedUniqueValues,
table_getGlobalFacetedMinMaxValues,
table_getGlobalFacetedRowModel,
table_getGlobalFacetedUniqueValues,
} from '../../../../src/static-functions'
import type { ColumnDef } from '../../../../src'

type Person = {
firstName: string
status: string | undefined
}

const features = {
...coreFeatures,
columnFacetingFeature,
columnFilteringFeature,
coreReactivityFeature: storeReactivityBindings(),
facetedMinMaxValues: createFacetedMinMaxValues(),
facetedRowModel: createFacetedRowModel(),
facetedUniqueValues: createFacetedUniqueValues(),
filteredRowModel: createFilteredRowModel(),
filterFns,
}

const columns: Array<ColumnDef<typeof features, Person, any>> = [
{
accessorKey: 'firstName',
id: 'firstName',
},
{
accessorKey: 'status',
filterFn: 'equalsString',
id: 'status',
},
]

const data: Array<Person> = [
{ firstName: 'Alice', status: 'active' },
{ firstName: 'Bob', status: undefined },
{ firstName: 'Carol', status: 'active' },
]

function makeTable(columnFilters: Array<{ id: string; value: unknown }>) {
return constructTable<typeof features, Person>({
data,
columns,
features,
initialState: {
columnFilters,
},
})
}

describe('column faceting row model', () => {
it('reuses the pre-filtered row model when only the faceted column is filtered', () => {
const table = makeTable([{ id: 'status', value: 'active' }])
const statusColumn = table.getColumn('status')!

expect(statusColumn.getFacetedRowModel()).toBe(
table.getPreFilteredRowModel(),
)
})

it('still applies filters from other columns', () => {
const table = makeTable([{ id: 'status', value: 'active' }])
const firstNameColumn = table.getColumn('firstName')!

expect(
firstNameColumn.getFacetedRowModel().rows.map((row) => row.original),
).toEqual([
{ firstName: 'Alice', status: 'active' },
{ firstName: 'Carol', status: 'active' },
])
})

it('counts unique values without dropping undefined facet values', () => {
const table = makeTable([])
const statusColumn = table.getColumn('status')!

expect(statusColumn.getFacetedUniqueValues()).toEqual(
new Map<any, number>([
['active', 2],
[undefined, 1],
]),
)
})

it('caches faceted factory functions per column and global context', () => {
const facetedRowModel = vi.fn(
(table: any, _columnId: string) => () => table.getPreFilteredRowModel(),
)
const facetedMinMaxValues = vi.fn(
(_table: any, _columnId: string) => () => [1, 2] as [number, number],
)
const facetedUniqueValues = vi.fn(
(_table: any, _columnId: string) => () =>
new Map<any, number>([['cached', 1]]),
)

const cacheFeatures = {
...coreFeatures,
coreReactivityFeature: storeReactivityBindings(),
facetedMinMaxValues,
facetedRowModel,
facetedUniqueValues,
}

const table = constructTable<any, Person>({
data,
columns: columns as any,
features: cacheFeatures,
}) as any
const statusColumn = table.getColumn('status')!

column_getFacetedRowModel(statusColumn, table)
column_getFacetedRowModel(statusColumn, table)
table_getGlobalFacetedRowModel(table)
table_getGlobalFacetedRowModel(table)

column_getFacetedMinMaxValues(statusColumn, table)
column_getFacetedMinMaxValues(statusColumn, table)
table_getGlobalFacetedMinMaxValues(table)
table_getGlobalFacetedMinMaxValues(table)

column_getFacetedUniqueValues(statusColumn, table)
column_getFacetedUniqueValues(statusColumn, table)
table_getGlobalFacetedUniqueValues(table)
table_getGlobalFacetedUniqueValues(table)

expect(facetedRowModel).toHaveBeenCalledTimes(2)
expect(facetedRowModel).toHaveBeenNthCalledWith(1, table, 'status')
expect(facetedRowModel).toHaveBeenNthCalledWith(2, table, '__global__')
expect(facetedMinMaxValues).toHaveBeenCalledTimes(2)
expect(facetedUniqueValues).toHaveBeenCalledTimes(2)
})
})
Loading
Loading