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
4 changes: 4 additions & 0 deletions examples/angular/row-selection/src/app/app.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<div class="p-2">
<button class="border rounded px-2" (click)="toggleEnableRowSelection()">
{{ enableRowSelection() ? 'Disable' : 'Enable' }} Row selection
</button>

<div class="h-2"></div>

<table [tanStackTable]="table">
Expand Down
8 changes: 7 additions & 1 deletion examples/angular/row-selection/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class App {
private readonly rowSelection = signal<RowSelectionState>({})
readonly globalFilter = signal<string>('')
readonly data = signal(makeData(10_000))
readonly enableRowSelection = signal(true)

readonly columns = columnHelper.columns([
columnHelper.display({
Expand Down Expand Up @@ -93,7 +94,8 @@ export class App {
state: {
rowSelection: this.rowSelection(),
},
enableRowSelection: true, // enable row selection for all rows

enableRowSelection: this.enableRowSelection(), // enable row selection for all rows
// enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row
onRowSelectionChange: (updaterOrValue) => {
this.rowSelection.set(
Expand Down Expand Up @@ -136,4 +138,8 @@ export class App {
refreshData(): void {
this.data.set(makeData(10_000))
}

toggleEnableRowSelection() {
this.enableRowSelection.update((value) => !value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class TableHeaderSelection {
<input
type="checkbox"
[checked]="context.row.getIsSelected()"
[disabled]="!context.row.getCanSelect()"
(change)="context.row.getToggleSelectedHandler()($event)"
/>
`,
Expand Down
4 changes: 2 additions & 2 deletions examples/solid/row-selection/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"vite-plugin-solid": "^2.11.10"
},
"dependencies": {
"@tanstack/solid-table": "^9.0.0-alpha.10",
"@tanstack/solid-devtools": "^0.7.26",
"@tanstack/solid-table-devtools": "9.0.0-alpha.11",
"@tanstack/solid-table": "^9.0.0-alpha.10",
"@tanstack/solid-table-devtools": "workspace:*",
"solid-js": "^1.9.11"
}
}
11 changes: 7 additions & 4 deletions examples/solid/row-selection/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const _features = tableFeatures({
function App() {
const [data, setData] = createSignal(makeData(1_000))
const refreshData = () => setData(makeData(100_000)) // stress test
const [enableRowSelection, setEnableRowSelection] = createSignal(true)

// Create table first with a placeholder for columns
let table: SolidTable<typeof _features, Person>
Expand Down Expand Up @@ -117,8 +118,6 @@ function App() {
},
]

const [enableRowSelection, setEnableRowSelection] = createSignal(true)

table = createTable({
_features,
_rowModels: {
Expand All @@ -136,8 +135,6 @@ function App() {
debugTable: true,
})

window.setEnable = setEnableRowSelection

return (
// <table.Subscribe
// selector={(state) => ({
Expand Down Expand Up @@ -336,6 +333,12 @@ function App() {
>
Log table.getSelectedRowModel().flatRows
</button>
<button
class="border rounded p-2 mb-2"
onClick={() => setEnableRowSelection((prev) => !prev)}
>
{enableRowSelection() ? 'Disable' : 'Enable'} Row Selection
</button>
</div>
<div>
<label>Row Selection State:</label>
Expand Down
13 changes: 12 additions & 1 deletion examples/vue/row-selection/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,26 @@ const columns = columnHelper.columns([
])

const data = ref(makeData(10))
const enableRowSelection = ref(true)

const rerender = () => {
data.value = makeData(10)
}

const toggleRowSelection = () => {
enableRowSelection.value = !enableRowSelection.value
}

const table = useTable(
{
_features,
_rowModels: {},
data,
columns,
enableRowSelection: true, //enable row selection for all rows
// enable row selection for all rows
get enableRowSelection() {
return enableRowSelection.value
},
// enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row
},
(state) => ({ rowSelection: state.rowSelection }),
Expand Down Expand Up @@ -152,6 +160,9 @@ const table = useTable(
</table>
<div class="h-4" />
<button @click="rerender" class="border p-2">Rerender</button>
<button @click="toggleRowSelection" class="border p-2">
{{ enableRowSelection ? 'Enable' : 'Disable' }} Row Selection
</button>
</div>
</template>

Expand Down
5 changes: 4 additions & 1 deletion knip.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"ignoreDependencies": ["@faker-js/faker"],
"ignoreWorkspaces": ["examples/**", "packages/table-core/tests/**"],
"ignoreWorkspaces": ["examples/**"],
"ignore": ["**/*benchmark*", "**/benchmarks/**"],
"workspaces": {
"packages/table-core": {
"ignore": ["**/tests/**"]
},
"packages/match-sorter-utils": {
"ignoreDependencies": ["remove-accents"]
},
Expand Down
77 changes: 49 additions & 28 deletions packages/angular-table/src/injectTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
Injector,
assertInInjectionContext,
computed,
effect,
inject,
isSignal,
signal,
untracked,
} from '@angular/core'
Expand Down Expand Up @@ -31,6 +31,10 @@ export type AngularTable<
* The selected state from the table store, based on the selector provided.
*/
readonly state: Signal<Readonly<TSelected>>
/**
* A signal that returns the entire table instance. Will update on table/options change.
*/
readonly value: Signal<AngularTable<TFeatures, TData, TSelected>>
/**
* Subscribe to changes in the table store with a custom selector.
*/
Expand Down Expand Up @@ -107,18 +111,18 @@ export function injectTable<
): AngularTable<TFeatures, TData, TSelected> {
assertInInjectionContext(injectTable)
const injector = inject(Injector)

const angularReactivityFeature = constructReactivityFeature({
createSignal: (value) => {
return signal(value) as any
},
createMemo: (fn) => {
return computed(() => fn())
},
isSignal: (value) => isSignal(value),
})
const count = 0

return lazyInit(() => {
const stateNotifier = signal(0)

const angularReactivityFeature = constructReactivityFeature({
// optionsNotifier: () => stateNotifier(),
stateNotifier: () => {
return stateNotifier()
},
})

const resolvedOptions: TableOptions<TFeatures, TData> = {
...options(),
_features: {
Expand All @@ -127,14 +131,17 @@ export function injectTable<
},
} as TableOptions<TFeatures, TData>

const table: AngularTable<TFeatures, TData, TSelected> = constructTable(
resolvedOptions,
) as AngularTable<TFeatures, TData, TSelected>
const table = constructTable(resolvedOptions) as AngularTable<
TFeatures,
TData,
TSelected
>

const updatedOptions = computed<TableOptions<TFeatures, TData>>(() => {
const tableOptionsValue = options()
const currentOptions = table.latestOptions
const result: TableOptions<TFeatures, TData> = {
...table.options,
...currentOptions,
...tableOptionsValue,
_features: {
...tableOptionsValue._features,
Expand All @@ -147,26 +154,34 @@ export function injectTable<
return result
})

const tableState = injectStore(
table.store,
(state: TableState<TFeatures>) => state,
effect(
() => {
const newOptions = updatedOptions()
untracked(() => table.setOptions(newOptions))
},
{ injector },
)

const tableSignalNotifier = computed(
const tableState = injectStore(table.store, (state) => state, { injector })
const tableOptions = injectStore(table.optionsStore, (state) => state, {
injector,
})

let firstRun = true
effect(
() => {
tableOptions()
tableState()
const newOptions = updatedOptions()
untracked(() => table.setOptions(newOptions))
untracked(() => table.baseStore.setState((prev) => ({ ...prev })))
return table
if (!firstRun) {
untracked(() => {
stateNotifier.update((n) => n + 1)
})
}
firstRun = false
},
{ equal: () => false },
{ injector },
)

// @ts-ignore
table.setTableNotifier(tableSignalNotifier)

table.Subscribe = function Subscribe<TSubSelected = {}>(props: {
selector: (state: TableState<TFeatures>) => TSubSelected
equal?: ValueEqualityFn<TSubSelected>
Expand All @@ -176,11 +191,17 @@ export function injectTable<
equal: props.equal,
})
}

Object.defineProperty(table, 'state', {
value: injectStore(table.store, selector, { injector }),
})

Object.defineProperty(table, 'value', {
value: computed(() => {
stateNotifier()
return table
}),
})

return table
})
}
33 changes: 23 additions & 10 deletions packages/angular-table/tests/angularReactivityFeature.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('angularReactivityFeature', () => {
const table = createTestTable()
const tablePropertyKeys = Object.keys(table)

describe('Table property reactivity', () => {
describe.skip('Table property reactivity', () => {
test.each(
tablePropertyKeys.map((property) => [
property,
Expand Down Expand Up @@ -74,7 +74,7 @@ describe('angularReactivityFeature', () => {
})
})

describe('Header property reactivity', () => {
describe.skip('Header property reactivity', () => {
const headers = table.getHeaderGroups()
headers.forEach((headerGroup, index) => {
const headerPropertyKeys = Object.keys(headerGroup)
Expand Down Expand Up @@ -112,7 +112,7 @@ describe('angularReactivityFeature', () => {
})
})

describe('Column property reactivity', () => {
describe.skip('Column property reactivity', () => {
const columns = table.getAllColumns()
columns.forEach((column, index) => {
const columnPropertyKeys = Object.keys(column).concat(
Expand All @@ -133,7 +133,7 @@ describe('angularReactivityFeature', () => {
})
})

describe('Row and cells property reactivity', () => {
describe.skip('Row and cells property reactivity', () => {
const flatRows = table.getRowModel().flatRows
flatRows.forEach((row, index) => {
const rowsPropertyKeys = Object.keys(row).concat(
Expand Down Expand Up @@ -174,11 +174,12 @@ describe('angularReactivityFeature', () => {
})

describe('Integration', () => {
test('methods works will be reactive effects', () => {
test('methods within effect will be re-trigger when options/state changes', () => {
const data = signal<Array<Data>>([{ id: '1', title: 'Title' }])
const table = createTestTable(data)
const isSelectedRow1Captor = vi.fn<(val: boolean) => void>()
const cellGetValueCaptor = vi.fn<(val: unknown) => void>()
const cellGetValueMemoizedCaptor = vi.fn<(val: unknown) => void>()
const columnIsVisibleCaptor = vi.fn<(val: boolean) => void>()

// This will test a case where you put in the effect a single cell property method
Expand All @@ -188,46 +189,58 @@ describe('angularReactivityFeature', () => {
() => table.getRowModel().rows[0]!.getAllCells()[0]!,
)

const cellGetValue = computed(() => cell().getValue())

TestBed.runInInjectionContext(() => {
effect(() => {
isSelectedRow1Captor(cell().row.getIsSelected())
})
effect(() => {
cellGetValueCaptor(cell().getValue())
})
effect(() => {
cellGetValueMemoizedCaptor(cellGetValue())
})
effect(() => {
columnIsVisibleCaptor(cell().column.getIsVisible())
})
})

TestBed.tick()
expect(isSelectedRow1Captor).toHaveBeenCalledTimes(1)
expect(cellGetValueMemoizedCaptor).toHaveBeenCalledTimes(1)
expect(cellGetValueCaptor).toHaveBeenCalledTimes(1)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(1)

cell().row.toggleSelected(true)
TestBed.tick()
expect(isSelectedRow1Captor).toHaveBeenCalledTimes(2)
expect(cellGetValueCaptor).toHaveBeenCalledTimes(1)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(1)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(2)

data.set([{ id: '1', title: 'Title 3' }])
TestBed.tick()
// In this case it will be called twice since `data` will change and
// the cell instance will be recreated
expect(isSelectedRow1Captor).toHaveBeenCalledTimes(3)
expect(cellGetValueCaptor).toHaveBeenCalledTimes(2)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(2)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(3)

cell().column.toggleVisibility(false)
TestBed.tick()
expect(isSelectedRow1Captor).toHaveBeenCalledTimes(3)
expect(isSelectedRow1Captor).toHaveBeenCalledTimes(4)
expect(cellGetValueCaptor).toHaveBeenCalledTimes(2)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(3)
expect(columnIsVisibleCaptor).toHaveBeenCalledTimes(4)

expect(isSelectedRow1Captor.mock.calls).toEqual([[false], [true], [true]])
expect(isSelectedRow1Captor.mock.calls).toEqual([
[false],
[true],
[true],
[true],
])
expect(cellGetValueCaptor.mock.calls).toEqual([['1'], ['1']])
expect(columnIsVisibleCaptor.mock.calls).toEqual([
[true],
[true],
[true],
[false],
Expand Down
Loading
Loading