Skip to content

Commit 018b0b8

Browse files
committed
add custom order
1 parent 12336de commit 018b0b8

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

next/src/custom/order.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Field } from '../field/type'
2+
import type { JsfSchema } from '../types'
3+
4+
function sort(params: {
5+
fields: Field[]
6+
order: string[]
7+
}): Field[] {
8+
const { fields: prevFields, order } = params
9+
10+
// Map from field name to expected index
11+
const indexMap: Record<string, number | undefined> = {}
12+
order.forEach((key, index) => {
13+
indexMap[key] = index
14+
})
15+
16+
const nextFields = prevFields.sort((a, b) => {
17+
// Compare by index
18+
const indexA = indexMap[a.name] ?? Infinity
19+
const indexB = indexMap[b.name] ?? Infinity
20+
21+
// The else actually only happens when both are Infinity,
22+
// i.e., not specified in the order array
23+
if (indexA !== indexB)
24+
return indexA - indexB
25+
26+
// If not specified, maintain original relative order
27+
return prevFields.indexOf(a) - prevFields.indexOf(b)
28+
})
29+
30+
return nextFields
31+
}
32+
33+
/**
34+
* Sort fields by schema's `x-jsf-order`
35+
*/
36+
export function setCustomOrder(params: {
37+
schema: JsfSchema
38+
fields: Field[]
39+
}): Field[] {
40+
const { schema, fields } = params
41+
42+
// TypeScript does not yield if we remove this check,
43+
// but it's only because our typing is likely not right.
44+
// See internal discussion:
45+
// - https://remote-com.slack.com/archives/C02HTN0LY02/p1738745237733389?thread_ts=1738741631.346809&cid=C02HTN0LY02
46+
if (typeof schema === 'boolean')
47+
throw new Error('Schema must be an object')
48+
49+
if (schema['x-jsf-order'] !== undefined)
50+
return sort({ fields, order: schema['x-jsf-order'] })
51+
52+
return fields
53+
}

next/test/custom/order.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type { JsfSchema } from '../../src/types'
2+
import { describe, expect, it } from '@jest/globals'
3+
import { createHeadlessForm } from '../../src'
4+
5+
describe('custom order', () => {
6+
it('should sort fields by x-jsf-order', () => {
7+
const schema: JsfSchema = {
8+
'type': 'object',
9+
'properties': {
10+
name: { type: 'string' },
11+
age: { type: 'number' },
12+
},
13+
'x-jsf-order': ['age', 'name'],
14+
}
15+
const form = createHeadlessForm(schema)
16+
17+
const keys = form.fields.map(field => field.name)
18+
expect(keys).toEqual(['age', 'name'])
19+
})
20+
21+
it('should sort nested objects', () => {
22+
const addressSchema: JsfSchema = {
23+
'type': 'object',
24+
'properties': {
25+
state: { type: 'string' },
26+
city: { type: 'string' },
27+
street: { type: 'string' },
28+
},
29+
'x-jsf-order': ['street', 'city', 'state'],
30+
}
31+
32+
const mainSchema: JsfSchema = {
33+
'type': 'object',
34+
'properties': {
35+
address: addressSchema,
36+
name: { type: 'string' },
37+
},
38+
'x-jsf-order': ['name', 'address'],
39+
}
40+
41+
const form = createHeadlessForm(mainSchema)
42+
43+
const mainKeys = form.fields.map(field => field.name)
44+
expect(mainKeys).toEqual(['name', 'address'])
45+
46+
const addressField = form.fields.find(field => field.name === 'address')
47+
if (addressField === undefined)
48+
throw new Error('Address field not found')
49+
50+
// This already throws if "fields" is undefined
51+
const addressKeys = addressField.fields?.map(field => field.name)
52+
expect(addressKeys).toEqual(['street', 'city', 'state'])
53+
})
54+
55+
it('should respect initial, unspecified order', () => {
56+
const schema: JsfSchema = {
57+
'type': 'object',
58+
'properties': {
59+
one: { type: 'string' },
60+
two: { type: 'string' },
61+
three: { type: 'string' },
62+
},
63+
'x-jsf-order': ['three'],
64+
}
65+
66+
const form = createHeadlessForm(schema)
67+
const keys = form.fields.map(field => field.name)
68+
69+
// "one" and "two" are not specified,
70+
// so they are added to the end,
71+
// respecting their relative initial order
72+
expect(keys).toEqual(['three', 'one', 'two'])
73+
})
74+
})

0 commit comments

Comments
 (0)