Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/raw-svelte-options-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/svelte-table': patch
---

Fix controlled `expanded` and `pagination` state resetting straight after each change. The options store now uses `$state.raw`, so an options sync no longer wraps `data` in a new proxy, which the core row model treated as new data and answered by running its auto-resets.
8 changes: 7 additions & 1 deletion packages/svelte-table/src/reactivity.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ function subscribeToRune<T>(
return { unsubscribe }
}

/**
* Backs the options store. Uses `$state.raw` because `set` always replaces the
* whole options object: a deep `$state` would wrap `options.data` in a new proxy
* on every set, which the row model treats as new data and resets controlled
* pagination/expanded state (#6640).
*/
function createRuneWritableAtom<T>(initialValue: T): Atom<T> {
let value = $state(initialValue)
let value = $state.raw(initialValue)

return {
set: (updater: T | ((prevVal: T) => T)) => {
Expand Down
21 changes: 21 additions & 0 deletions packages/svelte-table/tests/adapter-lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { describe, expect, test, vi } from 'vitest'
import { act, fireEvent, render, screen } from '@testing-library/svelte'
import { createAtom } from '@tanstack/svelte-store'
import CallbackHarness from './fixtures/CallbackHarness.svelte'
import ExpandingHarness from './fixtures/ExpandingHarness.svelte'
import PaginationHarness from './fixtures/PaginationHarness.svelte'
import ReactivityHarness from './fixtures/ReactivityHarness.svelte'
import RuneStateHarness from './fixtures/RuneStateHarness.svelte'
Expand Down Expand Up @@ -144,6 +145,26 @@ describe('Svelte adapter lifecycle and reactive options', () => {
expect(outputText('Paginated row ids')).toBe('0,1,2')
})

test('createTableState-controlled pagination is not reset by its own change', async () => {
render(PaginationHarness)

await fireEvent.click(screen.getByRole('button', { name: 'Next page' }))
await act()

expect(outputText('Paginated row ids')).toBe('5,6,7,8,9')
})

test('createTableState-controlled expanded state is not reset by its own change', async () => {
render(ExpandingHarness)

await fireEvent.click(
screen.getByRole('button', { name: 'Expand first row' }),
)
await act()

expect(outputText('Expanded row ids')).toBe('1')
})

test('native rune projections only rerun for their tracked dependency', async () => {
const selectedRowCaptor = vi.fn<(selected: boolean) => void>()
const selectionCaptor = vi.fn<(selection: RowSelectionState) => void>()
Expand Down
42 changes: 42 additions & 0 deletions packages/svelte-table/tests/fixtures/ExpandingHarness.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script lang="ts">
import { createExpandedRowModel, stockFeatures } from '@tanstack/table-core'
import { createTable } from '../../src/createTable.svelte'
import { createTableState } from '../../src/createTableState.svelte'
import type { ColumnDef, ExpandedState } from '@tanstack/table-core'

type Data = { id: string }

const features = {
...stockFeatures,
expandedRowModel: createExpandedRowModel(),
}
const columns: Array<ColumnDef<typeof features, Data>> = [
{ id: 'id', accessorKey: 'id' },
]
const data = [{ id: '1' }, { id: '2' }]
const [expanded, setExpanded] = createTableState<ExpandedState>({})
const table = createTable({
data,
columns,
features,
getRowId: (row) => row.id,
getRowCanExpand: () => true,
state: {
get expanded() {
return expanded()
},
},
onExpandedChange: setExpanded,
})
</script>

<output aria-label="Expanded row ids"
>{table
.getRowModel()
.rows.filter((row) => row.getIsExpanded())
.map((row) => row.id)
.join(',')}</output
>
<button onclick={() => table.getRow('1').toggleExpanded()}>
Expand first row
</button>
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@
.join(',')}</output
>
<button onclick={() => table.setPageSize(3)}>Show three rows</button>
<button onclick={() => table.nextPage()}>Next page</button>