From 44f5dd6636d4b23aa55383b8b8abe4c2f73bc88d Mon Sep 17 00:00:00 2001 From: Amey Pawar <138877912+ameyypawar@users.noreply.github.com> Date: Sun, 7 Jun 2026 13:25:12 +0530 Subject: [PATCH] fix(frontend): respect forced column order for numeric column names (#9463) --- .../src/lib/components/DisplayResult.svelte | 18 +++++++++++++ .../lib/components/table/AutoDataTable.svelte | 5 ++-- .../lib/components/table/tableUtils.test.ts | 25 +++++++++++++++++++ .../src/lib/components/table/tableUtils.ts | 10 ++++++-- 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 frontend/src/lib/components/table/tableUtils.test.ts diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index 3d94c33c21..707cda66e1 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -410,6 +410,23 @@ return json } + // The explicit column order from a leading header row (see + // handleArrayOfObjectsHeaders). Returned as an array so the order survives: + // baking it into object keys loses integer-like names like "1234", which JS + // enumerates first in ascending numeric order. + function getForcedColumnOrder(json: any): string[] | undefined { + if ( + Array.isArray(json) && + json.length > 0 && + Array.isArray(json[0]) && + json[0].length > 0 && + json[0].every((item) => typeof item === 'string') + ) { + return json[0] + } + return undefined + } + type InputObject = { [key: string]: number[] } function objectOfArraysToObjects(input: InputObject): any[] { @@ -643,6 +660,7 @@ ? 'absolute inset-0 [&>div]:h-full [&>div]:min-h-[10rem]' : ''} objects={handleArrayOfObjectsHeaders(data)} + headerOrder={getForcedColumnOrder(data)} /> {:else if !forceJson && resultKind === 'html'}
diff --git a/frontend/src/lib/components/table/AutoDataTable.svelte b/frontend/src/lib/components/table/AutoDataTable.svelte index b6f70d2307..ef835eb7f4 100644 --- a/frontend/src/lib/components/table/AutoDataTable.svelte +++ b/frontend/src/lib/components/table/AutoDataTable.svelte @@ -21,10 +21,11 @@ import DownloadCsv from './DownloadCsv.svelte' interface Props { objects?: Array> + headerOrder?: string[] class?: string } - let { objects = [], class: className }: Props = $props() + let { objects = [], headerOrder, class: className }: Props = $props() let currentPage = $state(1) let perPage = $state(25) @@ -37,7 +38,7 @@ let headers: string[] = $state([]) function recomputeObjectsAndHeaders(objects: Array>) { - ;[headers, structuredObjects] = computeStructuredObjectsAndHeaders(objects) + ;[headers, structuredObjects] = computeStructuredObjectsAndHeaders(objects, headerOrder) } function adjustCurrentPage() { diff --git a/frontend/src/lib/components/table/tableUtils.test.ts b/frontend/src/lib/components/table/tableUtils.test.ts new file mode 100644 index 0000000000..b34a142f19 --- /dev/null +++ b/frontend/src/lib/components/table/tableUtils.test.ts @@ -0,0 +1,25 @@ +import { describe, it, expect } from 'vitest' +import { computeStructuredObjectsAndHeaders } from './tableUtils' + +describe('computeStructuredObjectsAndHeaders', () => { + it('respects an explicit headerOrder when a column name is integer-like (#9183)', () => { + // "1234" must keep its specified position instead of jumping to the front, + // the way Object.keys() orders integer-like keys. + const headerOrder = ['Pokemon name', 'Type', '1234', 'Main strength'] + const rows = [ + { 'Pokemon name': 'Pikachu', Type: 'Electric', '1234': 'nil', 'Main strength': 'Speed' } + ] + const [headers] = computeStructuredObjectsAndHeaders(rows, headerOrder) + expect(headers).toEqual(['Pokemon name', 'Type', '1234', 'Main strength']) + }) + + it('appends keys not present in headerOrder, after the explicit ones', () => { + const [headers] = computeStructuredObjectsAndHeaders([{ a: 1, b: 2, c: 3 }], ['a', 'b']) + expect(headers).toEqual(['a', 'b', 'c']) + }) + + it('keeps the existing object-key behavior when no headerOrder is given', () => { + const [headers] = computeStructuredObjectsAndHeaders([{ x: 1, y: 2 }]) + expect(headers).toEqual(['x', 'y']) + }) +}) diff --git a/frontend/src/lib/components/table/tableUtils.ts b/frontend/src/lib/components/table/tableUtils.ts index db935ebe02..fa1d020c77 100644 --- a/frontend/src/lib/components/table/tableUtils.ts +++ b/frontend/src/lib/components/table/tableUtils.ts @@ -8,7 +8,10 @@ export function isEmail(value: string) { return value?.includes('@') } -export function computeStructuredObjectsAndHeaders(objects: Array>): [ +export function computeStructuredObjectsAndHeaders( + objects: Array>, + headerOrder?: string[] +): [ string[], { _id: number @@ -18,7 +21,10 @@ export function computeStructuredObjectsAndHeaders(objects: Array { let rowData = obj && typeof obj == 'object' ? obj : {} if (Array.isArray(rowData)) {