fix(kanban): preserve scroll on board re-init + propagate same-column reorders via SSE (#20637)
closes https://discord.com/channels/1130383047699738754/1504130730840821860 https://github.com/user-attachments/assets/d5833031-01c6-4e46-b699-c29c42435a53 ## Summary Fixes two related issues with the kanban (board view) collaboration experience: 1. **Scroll-to-top on every data change** — `triggerRecordBoardInitialQuery` always scrolled the board to the top, even when re-initializing for a single-record data change (SSE echo of your own mutation, a collaborator's update). Scroll reset only makes sense when the dataset itself changes (filter / sort / group). 2. **Same-column reorders by other users did not propagate** — the server's diff function stripped `FieldMetadataType.POSITION`, so position-only updates produced empty `updatedFields` and short-circuited event emission entirely. SSE clients never received them. ## What's in here - **Frontend** — `useTriggerRecordBoardInitialQuery` now exposes a `triggerRecordBoardInitialQueryWithoutScrollReset` variant; data-driven re-inits in `RecordBoardDataChangedEffect` use it, while genuine filter / sort / group changes keep the scroll-resetting `triggerRecordBoardInitialQuery`. `getRecordBoardEffectsForUpdateInputs` classifies each update as `trigger-initial-query` / `reposition-records` / `none`. For position- or group-only changes we skip the re-query and reposition records in place in the store (`useRepositionRecordsOnBoard`), which avoids the flicker and preserves scroll. - **Server** — removes `POSITION` from `objectRecordChangedValues`' strip list, so position-only updates emit a non-empty diff and flow through SSE. Position is now treated as a field like any other across all event consumers (SSE, webhooks, workflows, logic functions); a trigger with an explicit field filter still excludes it.
This commit is contained in:
+49
@@ -245,4 +245,53 @@ describe('transformEventBatchToWebhookEvents', () => {
|
||||
|
||||
expect(resultWithoutEventDate).toEqual(expectedResultWithoutEventDate);
|
||||
});
|
||||
|
||||
it('should include position-only update events', () => {
|
||||
const workspaceEventBatch: WorkspaceEventBatch<ObjectRecordEvent> = {
|
||||
workspaceId: 'workspaceId',
|
||||
objectMetadata: mockObjectMetadata,
|
||||
name: 'objectNameSingular.updated',
|
||||
events: [
|
||||
{
|
||||
recordId: 'recordId-1',
|
||||
properties: {
|
||||
after: { id: 'id-1', nameSingular: 'nameSingular-1' },
|
||||
updatedFields: ['position'],
|
||||
},
|
||||
},
|
||||
{
|
||||
recordId: 'recordId-2',
|
||||
properties: {
|
||||
after: { id: 'id-2', nameSingular: 'nameSingular-2' },
|
||||
updatedFields: ['nameSingular', 'position'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const webhooks = [
|
||||
{
|
||||
id: 'webhook-id',
|
||||
targetUrl: 'targetUrl',
|
||||
secret: 'secret',
|
||||
},
|
||||
] as WebhookEntity[];
|
||||
|
||||
const result = transformEventBatchToWebhookEvents({
|
||||
workspaceEventBatch,
|
||||
webhooks,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].record).toEqual({
|
||||
id: 'id-1',
|
||||
nameSingular: 'nameSingular-1',
|
||||
});
|
||||
expect(result[0].updatedFields).toEqual(['position']);
|
||||
expect(result[1].record).toEqual({
|
||||
id: 'id-2',
|
||||
nameSingular: 'nameSingular-2',
|
||||
});
|
||||
expect(result[1].updatedFields).toEqual(['nameSingular', 'position']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user