diff --git a/examples/angular/basic/src/app/app.component.html b/examples/angular/basic/src/app/app.component.html index 68c81953a6..8f105b6da5 100644 --- a/examples/angular/basic/src/app/app.component.html +++ b/examples/angular/basic/src/app/app.component.html @@ -24,7 +24,7 @@ @for (row of table.getRowModel().rows; track row.id) { - @for (cell of row.getVisibleCells(); track cell.id) { + @for (cell of row.getAllCells(); track cell.id) { ([]) + readonly data = signal(makeData(10000)) + readonly grouping = signal([]) - stringifiedGrouping = computed(() => JSON.stringify(this.grouping(), null, 2)) - - tableOptions = computed(() => - tableOptions({ - data: this.data(), - columns: columns, - state: { - grouping: this.grouping(), - }, - onGroupingChange: (updaterOrValue: Updater) => { - const groupingState = - typeof updaterOrValue === 'function' - ? updaterOrValue([...this.grouping()]) - : updaterOrValue - this.grouping.set(groupingState) - }, - getExpandedRowModel: createExpandedRowModel(), - getGroupedRowModel: createGroupedRowModel(), - getCoreRowModel: createCoreRowModel(), - getPaginatedRowModel: createPaginatedRowModel(), - getFilteredRowModel: createFilteredRowModel(), - debugTable: true, - }), + readonly stringifiedGrouping = computed(() => + JSON.stringify(this.grouping(), null, 2), ) - table = injectTable(this.tableOptions) + readonly table = injectTable(() => ({ + data: this.data(), + columns: columns, + initialState: { + pagination: { pageSize: 20, pageIndex: 0 }, + }, + state: { + grouping: this.grouping(), + }, + _features: { + columnGroupingFeature, + rowPaginationFeature, + columnFilteringFeature, + rowExpandingFeature, + }, + _rowModels: { + groupedRowModel: createGroupedRowModel(), + expandedRowModel: createExpandedRowModel(), + paginatedRowModel: createPaginatedRowModel(), + filteredRowModel: createFilteredRowModel(), + }, + onGroupingChange: (updaterOrValue: Updater) => { + const groupingState = + typeof updaterOrValue === 'function' + ? updaterOrValue([...this.grouping()]) + : updaterOrValue + this.grouping.set(groupingState) + }, + })) onPageInputChange(event: any): void { const page = event.target.value ? Number(event.target.value) - 1 : 0 diff --git a/examples/angular/row-selection-signal/src/app/app.component.html b/examples/angular/row-selection-signal/src/app/app.component.html index c2dfb725f7..f50c2d0052 100644 --- a/examples/angular/row-selection-signal/src/app/app.component.html +++ b/examples/angular/row-selection-signal/src/app/app.component.html @@ -20,8 +20,9 @@ @if (header.column.getCanFilter()) {
+
diff --git a/examples/angular/row-selection-signal/src/app/app.component.ts b/examples/angular/row-selection-signal/src/app/app.component.ts index 908f667cdf..8c480ee9d2 100644 --- a/examples/angular/row-selection-signal/src/app/app.component.ts +++ b/examples/angular/row-selection-signal/src/app/app.component.ts @@ -7,10 +7,13 @@ import { } from '@angular/core' import { FlexRenderDirective, - createCoreRowModel, + columnFilteringFeature, + columnVisibilityFeature, createFilteredRowModel, createPaginatedRowModel, injectTable, + rowPaginationFeature, + rowSelectionFeature, } from '@tanstack/angular-table' import { FilterComponent } from './filter' import { makeData } from './makeData' @@ -97,6 +100,16 @@ export class AppComponent { table = injectTable(() => ({ data: this.data(), + _features: { + rowSelectionFeature, + rowPaginationFeature, + columnFilteringFeature, + columnVisibilityFeature, + }, + _rowModels: { + filteredRowModel: createFilteredRowModel(), + paginatedRowModel: createPaginatedRowModel(), + }, columns: this.columns, state: { rowSelection: this.rowSelection(), @@ -110,9 +123,6 @@ export class AppComponent { : updaterOrValue, ) }, - getCoreRowModel: createCoreRowModel(), - getFilteredRowModel: createFilteredRowModel(), - getPaginatedRowModel: createPaginatedRowModel(), debugTable: true, })) diff --git a/examples/angular/row-selection-signal/src/app/filter.ts b/examples/angular/row-selection-signal/src/app/filter.ts index bd3319c687..c21fcc3807 100644 --- a/examples/angular/row-selection-signal/src/app/filter.ts +++ b/examples/angular/row-selection-signal/src/app/filter.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common' import { Component, input } from '@angular/core' import type { OnInit } from '@angular/core' -import type { Column, Table } from '@tanstack/angular-table' +import type { Column, RowData, Table } from '@tanstack/angular-table' @Component({ selector: 'app-table-filter', @@ -39,10 +39,10 @@ import type { Column, Table } from '@tanstack/angular-table' standalone: true, imports: [CommonModule], }) -export class FilterComponent implements OnInit { +export class FilterComponent implements OnInit { column = input.required>() - table = input.required>() + table = input.required>() columnType!: string diff --git a/examples/angular/row-selection-signal/src/app/selection-column.component.ts b/examples/angular/row-selection-signal/src/app/selection-column.component.ts index 50d057b84c..2a6cb75670 100644 --- a/examples/angular/row-selection-signal/src/app/selection-column.component.ts +++ b/examples/angular/row-selection-signal/src/app/selection-column.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core' -import type { Row, Table } from '@tanstack/angular-table' +import type { Row, RowData, Table } from '@tanstack/angular-table' @Component({ template: ` @@ -16,7 +16,7 @@ import type { Row, Table } from '@tanstack/angular-table' standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TableHeadSelectionComponent { +export class TableHeadSelectionComponent { // Your component should also reflect the fields you use as props in flexRenderer directive. // Define the fields as input you want to use in your component // ie. In this case, you are passing HeaderContext object as props in flexRenderer directive. @@ -25,7 +25,7 @@ export class TableHeadSelectionComponent { // column = input.required>() // header = input.required>() - table = input.required>() + table = input.required>() } @Component({ @@ -42,6 +42,6 @@ export class TableHeadSelectionComponent { standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TableRowSelectionComponent { - row = input.required>() +export class TableRowSelectionComponent { + row = input.required>() } diff --git a/examples/angular/row-selection/src/app/app.component.html b/examples/angular/row-selection/src/app/app.component.html index c2dfb725f7..109bc93f63 100644 --- a/examples/angular/row-selection/src/app/app.component.html +++ b/examples/angular/row-selection/src/app/app.component.html @@ -20,8 +20,9 @@ @if (header.column.getCanFilter()) {
+
@@ -35,7 +36,7 @@ @for (row of table.getRowModel().rows; track row.id) { - @for (cell of row.getVisibleCells(); track cell.id) { + @for (cell of row.getAllCells(); track cell.id) { = Omit< + TableOptions>, + 'columns' | 'data' | 'state' +> & { + _features: TFeatures + TData?: TData // provide a cast for the TData type +} + +/** + * Internal type that each adapter package will build off of to create a table helper + */ +export type TableHelper_Core< + TFeatures extends TableFeatures, + TData extends RowData = any, +> = { + columnHelper: ColumnHelper + createColumnHelper: () => ColumnHelper< + TFeatures, + TData + > + features: TFeatures + options: Omit, 'columns' | 'data' | 'state'> + tableCreator: ( + tableOptions: () => Omit< + TableOptions, + '_features' | '_rowModels' | '_rowModelFns' + >, + ) => Table +} + +/** + * Internal function to create a table helper that each adapter package will use to create their own table helper + */ +export function constructTableHelper< + TFeatures extends TableFeatures, + TData extends RowData = any, +>( + tableCreator: ( + tableOptions: () => TableOptions, + ) => Table & Signal>, + tableHelperOptions: TableHelperOptions, +): TableHelper_Core { + const { TData: _TData, ..._tableHelperOptions } = tableHelperOptions + return { + columnHelper: createColumnHelper(), + createColumnHelper, + features: tableHelperOptions._features, + options: _tableHelperOptions as any, + tableCreator: (tableOptions) => + // @ts-expect-error Fix this + tableCreator(() => ({ + ...tableHelperOptions, + ...tableOptions(), + })), + } +} + +// test + +// // eslint-disable-next-line import/first, import/order +// import { constructTable } from '../core/table/constructTable' +// // eslint-disable-next-line import/first, import/order +// import { type ColumnDef } from '../types/ColumnDef' + +// type Person = { +// firstName: string +// lastName: string +// age: number +// } + +// const tableHelper = constructTableHelper(constructTable, { +// _features: { rowSelectionFeature: {} }, +// _rowModels: {}, +// TData: {} as Person, +// }) + +// const columns = [ +// tableHelper.columnHelper.accessor('firstName', { +// header: 'First Name', +// cell: (info) => info.getValue(), +// }), +// tableHelper.columnHelper.accessor('lastName', { header: 'Last Name' }), +// tableHelper.columnHelper.accessor('age', { header: 'Age' }), +// tableHelper.columnHelper.display({ header: 'Actions', id: 'actions' }), +// ] as Array> + +// const data: Array = [] + +// tableHelper.tableCreator({ +// columns, +// data, +// }) diff --git a/packages/angular-table/src/createTableHelper.ts b/packages/angular-table/src/createTableHelper.ts new file mode 100644 index 0000000000..b4f6d2a787 --- /dev/null +++ b/packages/angular-table/src/createTableHelper.ts @@ -0,0 +1,68 @@ +import { Signal } from '@angular/core' +import { constructTableHelper } from './constructTableHelper' +import { injectTable } from './injectTable' +import type { + RowData, + Table, + TableFeatures, + TableHelperOptions, + TableHelper_Core, + TableOptions, +} from '@tanstack/table-core' + +export type TableHelper< + TFeatures extends TableFeatures, + TData extends RowData = any, +> = Omit, 'tableCreator'> & { + injectTable: ( + tableOptions: () => Omit< + TableOptions, + '_features' | '_rowModels' | '_rowModelFns' + >, + ) => Table +} + +export function createTableHelper< + TFeatures extends TableFeatures, + TData extends RowData = any, +>( + tableHelperOptions: TableHelperOptions, +): TableHelper { + const tableHelper = constructTableHelper( + injectTable as unknown as ( + tableOptions: () => TableOptions, + ) => Table & Signal>, + tableHelperOptions, + ) + return { + ...tableHelper, + injectTable: tableHelper.tableCreator, + } as any +} + +// test + +// type Person = { +// firstName: string +// lastName: string +// age: number +// } + +// const tableHelper = createTableHelper({ +// _features: { rowSelectionFeature: {} }, +// TData: {} as Person, +// }) + +// const columns = [ +// tableHelper.columnHelper.accessor('firstName', { header: 'First Name' }), +// tableHelper.columnHelper.accessor('lastName', { header: 'Last Name' }), +// tableHelper.columnHelper.accessor('age', { header: 'Age' }), +// tableHelper.columnHelper.display({ header: 'Actions', id: 'actions' }), +// ] as Array> + +// const data: Array = [] + +// tableHelper.createTable({ +// columns, +// data, +// }) diff --git a/packages/angular-table/src/flex-render.ts b/packages/angular-table/src/flex-render.ts index a584af7ac6..70b36a829a 100644 --- a/packages/angular-table/src/flex-render.ts +++ b/packages/angular-table/src/flex-render.ts @@ -13,7 +13,8 @@ import { inject, isSignal, } from '@angular/core' -import type { OnChanges, SimpleChanges } from '@angular/core' +import type { DoCheck, OnChanges, SimpleChanges } from '@angular/core' +import type { Table } from '@tanstack/table-core' export type FlexRenderContent> = | string @@ -29,7 +30,7 @@ export type FlexRenderContent> = standalone: true, }) export class FlexRenderDirective> - implements OnChanges + implements OnChanges, DoCheck { @Input({ required: true, alias: 'flexRender' }) content: @@ -45,6 +46,8 @@ export class FlexRenderDirective> @Input({ required: false, alias: 'flexRenderInjector' }) injector: Injector = inject(Injector) + ref?: ComponentRef | EmbeddedViewRef | null = null + constructor( @Inject(ViewContainerRef) private readonly viewContainerRef: ViewContainerRef, @@ -52,12 +55,13 @@ export class FlexRenderDirective> private readonly templateRef: TemplateRef, ) {} - ref?: ComponentRef | EmbeddedViewRef | null = null - - ngOnChanges(changes: SimpleChanges) { + ngDoCheck(): void { if (this.ref instanceof ComponentRef) { this.ref.injector.get(ChangeDetectorRef).markForCheck() } + } + + ngOnChanges(changes: SimpleChanges) { if (!changes['content']) { return } @@ -69,12 +73,11 @@ export class FlexRenderDirective> const { content, props } = this if (content === null || content === undefined) { this.ref = null - return } if (typeof content === 'function') { - return this.renderContent(content(props)) + this.ref = this.renderContent(content(props)) } else { - return this.renderContent(content) + this.ref = this.renderContent(content) } } diff --git a/packages/angular-table/src/index.ts b/packages/angular-table/src/index.ts index b0b52b8882..f9bd9c806d 100644 --- a/packages/angular-table/src/index.ts +++ b/packages/angular-table/src/index.ts @@ -4,3 +4,4 @@ export * from './flex-render' export * from './proxy' export * from './lazy-signal-initializer' export * from './injectTable' +export * from './createTableHelper' diff --git a/packages/angular-table/src/injectTable.ts b/packages/angular-table/src/injectTable.ts index 748299f34b..f53b3ab95e 100644 --- a/packages/angular-table/src/injectTable.ts +++ b/packages/angular-table/src/injectTable.ts @@ -7,40 +7,52 @@ import { } from '@tanstack/table-core' import { lazyInit } from './lazy-signal-initializer' import { proxifyTable } from './proxy' -import type { Signal } from '@angular/core' import type { + CreateRowModels_All, RowData, Table, TableFeatures, TableOptions, TableState, } from '@tanstack/table-core' +import type { Signal } from '@angular/core' + +export type AngularTableOptions< + TFeatures extends TableFeatures, + TData extends RowData, +> = Omit, '_rowModels'> & { + _rowModels?: CreateRowModels_All + // TODO: no exported + // _rowModelsFns: RowModelFns +} export function injectTable< TFeatures extends TableFeatures, TData extends RowData, >( - options: () => TableOptions, + options: () => AngularTableOptions, ): Table & Signal> { return lazyInit(() => { - const resolvedOptions = { - ...options(), - _features: { + const features = () => { + return { ...coreFeatures, ...options()._features, - }, + } } - const table = constructTable(resolvedOptions) - // By default, manage table state here using the table's initial state const state = signal>( - getInitialTableState( - resolvedOptions._features, - resolvedOptions.initialState, - ), + getInitialTableState(features(), options().initialState), ) + const resolvedOptions: TableOptions = { + ...options(), + _features: features(), + state: { ...state(), ...options().state }, + } as TableOptions + + const table = constructTable(resolvedOptions) + // Compose table options using computed. // This is to allow `tableSignal` to listen and set table option const updatedOptions = computed>(() => { @@ -48,10 +60,12 @@ export function injectTable< const tableState = state() // listen to input options changed const tableOptions = options() + return { ...table.options, ...resolvedOptions, ...tableOptions, + _features: features(), state: { ...tableState, ...tableOptions.state }, onStateChange: (updater) => { const value = isFunction(updater) ? updater(tableState) : updater diff --git a/packages/angular-table/src/proxy.ts b/packages/angular-table/src/proxy.ts index 86a65434ab..04bf80f522 100644 --- a/packages/angular-table/src/proxy.ts +++ b/packages/angular-table/src/proxy.ts @@ -19,9 +19,9 @@ export function proxifyTable< apply() { return tableSignal() }, - get(target, property: keyof Table): any { - if (target[property]) { - return target[property] + get(target, property): any { + if (Reflect.has(target, property)) { + return Reflect.get(target, property) } const table = untracked(tableSignal) /** @@ -29,25 +29,25 @@ export function proxifyTable< * excluding handlers as they do not retain any reactive value */ if ( + typeof property === 'string' && property.startsWith('get') && !property.endsWith('Handler') && !property.endsWith('Model') ) { - const maybeFn = table[property] as Function | never + const maybeFn = (table as any)[property] as Function | never if (typeof maybeFn === 'function') { Object.defineProperty(target, property, { value: toComputed(tableSignal, maybeFn), configurable: true, enumerable: true, }) - return target[property] + return (target as any)[property] } } - // @ts-expect-error - return (target[property] = table[property]) + return ((target as any)[property] = (table as any)[property]) }, - has(_, prop: keyof Table) { - return !!untracked(tableSignal)[prop] + has(_, prop) { + return Reflect.has(untracked(tableSignal), prop) }, ownKeys() { return Reflect.ownKeys(untracked(tableSignal)) diff --git a/packages/angular-table/tests/createTableHelper.test-d.ts b/packages/angular-table/tests/createTableHelper.test-d.ts new file mode 100644 index 0000000000..e8a22c9555 --- /dev/null +++ b/packages/angular-table/tests/createTableHelper.test-d.ts @@ -0,0 +1,63 @@ +import { expectTypeOf, test } from 'vitest' +import { + createPaginatedRowModel, + createTableHelper, + stockFeatures, +} from '../src' +import type { ColumnDef, StockTableFeatures, Table, TableHelper } from '../src' + +test('infer data type from TData', () => { + type TestDataType = { firstName: string; lastName: string; age: number } + + const tableHelper = createTableHelper({ + _features: stockFeatures, + _rowModels: { + paginatedRowModel: createPaginatedRowModel(), + }, + TData: {} as TestDataType, + }) + + expectTypeOf().toEqualTypeOf< + TableHelper, TestDataType> + >() + + expectTypeOf<(typeof tableHelper)['features']>().toEqualTypeOf< + Required + >() + + const columns = [ + tableHelper.columnHelper.accessor('firstName', { header: 'First Name' }), + tableHelper.columnHelper.accessor('lastName', { header: 'Last Name' }), + tableHelper.columnHelper.accessor('age', { header: 'Age' }), + tableHelper.columnHelper.display({ header: 'Actions', id: 'actions' }), + ] as const + + expectTypeOf().toMatchTypeOf< + ReadonlyArray> + >() +}) + +test('infer data type given by injectTable', () => { + type TestDataType = { firstName: string; lastName: string } + + const tableHelper = createTableHelper({ + _features: stockFeatures, + _rowModels: { + paginatedRowModel: createPaginatedRowModel(), + }, + }) + + expectTypeOf().toEqualTypeOf< + TableHelper, any> + >() + + const injectTable = tableHelper.injectTable + const table = injectTable(() => ({ + data: [] as Array, + columns: [], + })) + + expectTypeOf().toEqualTypeOf< + Table, TestDataType> + >() +}) diff --git a/packages/angular-table/tests/flex-render.test.ts b/packages/angular-table/tests/flex-render.test.ts index 02e3c33003..c88bbc1e16 100644 --- a/packages/angular-table/tests/flex-render.test.ts +++ b/packages/angular-table/tests/flex-render.test.ts @@ -1,5 +1,5 @@ -import { Component, ViewChild, input, type TemplateRef } from '@angular/core' -import { TestBed, type ComponentFixture } from '@angular/core/testing' +import { Component, ViewChild, input } from '@angular/core' +import { TestBed } from '@angular/core/testing' import { createColumnHelper } from '@tanstack/table-core' import { describe, expect, test } from 'vitest' import { @@ -8,6 +8,8 @@ import { injectFlexRenderContext, } from '../src/flex-render' import { setFixtureSignalInput, setFixtureSignalInputs } from './test-utils' +import type { ComponentFixture } from '@angular/core/testing' +import type { TemplateRef } from '@angular/core' interface Data { id: string @@ -18,7 +20,7 @@ interface Data { } describe('FlexRenderDirective', () => { - const helper = createColumnHelper() + const helper = createColumnHelper<{}, Data>() test('should render primitives', async () => { const fixture = TestBed.createComponent(TestRenderComponent) diff --git a/packages/angular-table/tests/createAngularTable.test.ts b/packages/angular-table/tests/injectTable.test.ts similarity index 70% rename from packages/angular-table/tests/createAngularTable.test.ts rename to packages/angular-table/tests/injectTable.test.ts index 1ab38422df..55630f0eb5 100644 --- a/packages/angular-table/tests/createAngularTable.test.ts +++ b/packages/angular-table/tests/injectTable.test.ts @@ -1,9 +1,13 @@ import { describe, expect, test } from 'vitest' import { Component, input, isSignal, signal, untracked } from '@angular/core' import { TestBed } from '@angular/core/testing' -import { createCoreRowModel, injectTable } from '../src/injectTable' -import { setSignalInputs } from './test-utils' -import type { ColumnDef, Table } from '../src/injectTable' +import { ColumnDef, stockFeatures } from '@tanstack/table-core' +import { injectTable } from '../src/injectTable' +import { + experimentalReactivity_testShouldBeComputedProperty, + setSignalInputs, + testShouldBeComputedProperty, +} from './test-utils' describe('injectTable', () => { test('should render with required signal inputs', () => { @@ -17,8 +21,8 @@ describe('injectTable', () => { table = injectTable(() => ({ data: this.data(), + _features: stockFeatures, columns: [], - getCoreRowModel: createCoreRowModel(), })) } @@ -39,8 +43,8 @@ describe('injectTable', () => { ] const table = injectTable(() => ({ data: data(), + _features: stockFeatures, columns: columns, - getCoreRowModel: createCoreRowModel(), getRowId: (row) => row.id, })) const tablePropertyKeys = Object.keys(table()) @@ -50,7 +54,7 @@ describe('injectTable', () => { }) test('supports "in" operator', () => { - expect('getCoreRowModel' in table).toBe(true) + expect('_features' in table).toBe(true) expect('options' in table).toBe(true) expect('notFound' in table).toBe(false) }) @@ -71,21 +75,3 @@ describe('injectTable', () => { }) }) }) - -const testShouldBeComputedProperty = ( - table: Table, - propertyName: string, -) => { - if (propertyName.endsWith('Handler') || propertyName.endsWith('Model')) { - return false - } - - if (propertyName.startsWith('get')) { - // Only properties with no arguments are computed - const fn = table[propertyName as keyof Table] - // Cannot test if is lazy computed since we return the unwrapped value - return fn instanceof Function && fn.length === 0 - } - - return false -} diff --git a/packages/angular-table/tests/lazy-init.test.ts b/packages/angular-table/tests/lazy-init.test.ts index fa3a43f553..ba9854fb2b 100644 --- a/packages/angular-table/tests/lazy-init.test.ts +++ b/packages/angular-table/tests/lazy-init.test.ts @@ -2,7 +2,6 @@ import { describe, expect, test, vi } from 'vitest' import { ChangeDetectionStrategy, Component, - type WritableSignal, computed, effect, input, @@ -11,6 +10,7 @@ import { import { TestBed } from '@angular/core/testing' import { lazyInit } from '../src/lazy-signal-initializer' import { flushQueue, setFixtureSignalInputs } from './test-utils' +import type { WritableSignal } from '@angular/core' describe('lazyInit', () => { test('should init lazily in next tick when not accessing manually', async () => { @@ -51,7 +51,7 @@ describe('lazyInit', () => { test('should init lazily and only once', async () => { const initCallFn = vi.fn() - const registerDataValue = vi.fn<[number]>() + const registerDataValue = vi.fn<(arg0: number) => void>() let value!: { data: WritableSignal } const outerSignal = signal(0) @@ -84,7 +84,7 @@ describe('lazyInit', () => { await flushQueue() expect(initCallFn).toHaveBeenCalledTimes(1) - expect(registerDataValue).toHaveBeenCalledTimes(2) + expect(registerDataValue).toHaveBeenCalledTimes(1) }) test('should support required signal input', async () => { diff --git a/packages/angular-table/tests/test-utils.ts b/packages/angular-table/tests/test-utils.ts index 28b788e0bf..443f091a06 100644 --- a/packages/angular-table/tests/test-utils.ts +++ b/packages/angular-table/tests/test-utils.ts @@ -1,6 +1,7 @@ -import type { InputSignal } from '@angular/core' import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals' +import type { InputSignal } from '@angular/core' import type { ComponentFixture } from '@angular/core/testing' +import { Table } from '@tanstack/table-core' type ToSignalInputUpdatableMap = { [K in keyof T as T[K] extends InputSignal @@ -60,3 +61,42 @@ function componentHasSignalInputProperty( export async function flushQueue() { await new Promise(setImmediate) } + +export const experimentalReactivity_testShouldBeComputedProperty = ( + testObj: any, + propertyName: string, +) => { + if (propertyName.startsWith('_rootNotifier')) { + return true + } + if (propertyName.endsWith('Handler')) { + return false + } + + if (propertyName.startsWith('get')) { + // Only properties with no arguments are computed + const fn = testObj[propertyName] + // Cannot test if is lazy computed since we return the unwrapped value + return fn instanceof Function && fn.length === 0 + } + + return false +} + +export const testShouldBeComputedProperty = ( + testObj: any, + propertyName: string, +) => { + if (propertyName.endsWith('Handler') || propertyName.endsWith('Model')) { + return false + } + + if (propertyName.startsWith('get')) { + // Only properties with no arguments are computed + const fn = testObj[propertyName] + // Cannot test if is lazy computed since we return the unwrapped value + return fn instanceof Function && fn.length === 0 + } + + return false +} diff --git a/packages/angular-table/vite.config.ts b/packages/angular-table/vite.config.ts index 523b22e583..1ba318b695 100644 --- a/packages/angular-table/vite.config.ts +++ b/packages/angular-table/vite.config.ts @@ -9,5 +9,9 @@ export default defineConfig({ environment: 'jsdom', setupFiles: ['./tests/test-setup.ts'], globals: true, + typecheck: { + enabled: true, + tsconfig: './tsconfig.json', + }, }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7051a5c73d..f5baad1c9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,7 +70,7 @@ importers: version: 5.4.11(@types/node@22.9.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) vitest: specifier: ^2.1.5 - version: 2.1.5(@types/node@22.9.1)(jsdom@25.0.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + version: 2.1.5(@types/node@22.9.1)(@vitest/ui@2.1.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) examples/angular/basic: dependencies: @@ -2955,6 +2955,9 @@ importers: '@angular/platform-browser-dynamic': specifier: ^19.0.0 version: 19.0.0(@angular/common@19.0.0(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/compiler@19.0.0(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0(@angular/animations@19.0.0(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0))) + '@vitest/ui': + specifier: ^2.1.5 + version: 2.1.5(vitest@2.1.5) ng-packagr: specifier: ^19.0.0 version: 19.0.0(@angular/compiler-cli@19.0.0(@angular/compiler@19.0.0(@angular/core@19.0.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(tslib@2.8.1)(typescript@5.6.3) @@ -5102,6 +5105,9 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -5812,6 +5818,11 @@ packages: '@vitest/spy@2.1.5': resolution: {integrity: sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==} + '@vitest/ui@2.1.5': + resolution: {integrity: sha512-ERgKkDMTfngrZip6VG5h8L9B5D0AH/4+bga4yR1UzGH7c2cxv3LWogw2Dvuwr9cP3/iKDHYys7kIFLDKpxORTg==} + peerDependencies: + vitest: 2.1.5 + '@vitest/utils@2.1.5': resolution: {integrity: sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==} @@ -7182,6 +7193,9 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -9394,6 +9408,10 @@ packages: simple-git@3.27.0: resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==} + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + size-limit@11.1.6: resolution: {integrity: sha512-S5ux2IB8rU26xwVgMskmknGMFkieaIAqDLuwgKiypk6oa4lFsie8yFPrzRFV+yrLDY2GddjXuCaVk5PveVOHiQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9761,6 +9779,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + tough-cookie@5.0.0: resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} engines: {node: '>=16'} @@ -12697,6 +12719,8 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@polka/url@1.0.0-next.28': {} + '@popperjs/core@2.11.8': {} '@rollup/plugin-json@6.1.0(rollup@4.26.0)': @@ -13516,6 +13540,17 @@ snapshots: dependencies: tinyspy: 3.0.2 + '@vitest/ui@2.1.5(vitest@2.1.5)': + dependencies: + '@vitest/utils': 2.1.5 + fflate: 0.8.2 + flatted: 3.3.1 + pathe: 1.1.2 + sirv: 3.0.0 + tinyglobby: 0.2.10 + tinyrainbow: 1.2.0 + vitest: 2.1.5(@types/node@22.9.1)(@vitest/ui@2.1.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + '@vitest/utils@2.1.5': dependencies: '@vitest/pretty-format': 2.1.5 @@ -15217,6 +15252,8 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fflate@0.8.2: {} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -17633,6 +17670,12 @@ snapshots: transitivePeerDependencies: - supports-color + sirv@3.0.0: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + size-limit@11.1.6: dependencies: bytes-iec: 3.1.1 @@ -18030,6 +18073,8 @@ snapshots: toidentifier@1.0.1: {} + totalist@3.0.1: {} + tough-cookie@5.0.0: dependencies: tldts: 6.1.61 @@ -18350,7 +18395,7 @@ snapshots: optionalDependencies: vite: 5.4.11(@types/node@22.9.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) - vitest@2.1.5(@types/node@22.9.1)(jsdom@25.0.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0): + vitest@2.1.5(@types/node@22.9.1)(@vitest/ui@2.1.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0): dependencies: '@vitest/expect': 2.1.5 '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.9.1)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) @@ -18374,6 +18419,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.9.1 + '@vitest/ui': 2.1.5(vitest@2.1.5) jsdom: 25.0.1 transitivePeerDependencies: - less