fix(frontend): respect forced column order for numeric column names (#9463)

This commit is contained in:
Amey Pawar
2026-06-07 07:55:12 +00:00
committed by GitHub
parent 004339032e
commit 44f5dd6636
4 changed files with 54 additions and 4 deletions
@@ -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'}
<div class="h-full">
@@ -21,10 +21,11 @@
import DownloadCsv from './DownloadCsv.svelte'
interface Props {
objects?: Array<Record<string, any>>
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<Record<string, any>>) {
;[headers, structuredObjects] = computeStructuredObjectsAndHeaders(objects)
;[headers, structuredObjects] = computeStructuredObjectsAndHeaders(objects, headerOrder)
}
function adjustCurrentPage() {
@@ -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'])
})
})
@@ -8,7 +8,10 @@ export function isEmail(value: string) {
return value?.includes('@')
}
export function computeStructuredObjectsAndHeaders(objects: Array<Record<string, any>>): [
export function computeStructuredObjectsAndHeaders(
objects: Array<Record<string, any>>,
headerOrder?: string[]
): [
string[],
{
_id: number
@@ -18,7 +21,10 @@ export function computeStructuredObjectsAndHeaders(objects: Array<Record<string,
if (Array.isArray(objects)) {
let nextId = 1
let hds: string[] = []
// Seed headers from the explicit order when provided. Object key order
// can't be trusted here: integer-like keys (e.g. "1234") are enumerated
// first in ascending numeric order regardless of insertion order.
let hds: string[] = headerOrder ? [...headerOrder] : []
let objs = objects.map((obj) => {
let rowData = obj && typeof obj == 'object' ? obj : {}
if (Array.isArray(rowData)) {