diff --git a/libs/util/array.spec.ts b/libs/util/array.spec.tsx similarity index 57% rename from libs/util/array.spec.ts rename to libs/util/array.spec.tsx index 10ff1373f4..d948eccf54 100644 --- a/libs/util/array.spec.ts +++ b/libs/util/array.spec.tsx @@ -5,6 +5,7 @@ * * Copyright Oxide Computer Company */ +import { type ReactElement } from 'react' import { expect, test } from 'vitest' import { groupBy, intersperse, lowestBy, sortBy, sumBy } from './array' @@ -68,12 +69,31 @@ test('sumBy', () => { }) test('intersperse', () => { - expect(intersperse([], 'x')).toEqual([]) - expect(intersperse(['a'], 'x')).toEqual(['a']) + expect(intersperse([], <>,)).toEqual([]) - expect(intersperse(['a', 'b'], ',')).toEqual(['a', ',', 'b']) - expect(intersperse(['a', 'b'], ',', 'or')).toEqual(['a', 'or', 'b']) + const a = a + const b = b + const c = c + const comma = <>, + const or = <>or - expect(intersperse(['a', 'b', 'c'], ',')).toEqual(['a', ',', 'b', ',', 'c']) - expect(intersperse(['a', 'b', 'c'], ',', 'or')).toEqual(['a', ',', 'b', ',', 'or', 'c']) + const getText = (el: ReactElement) => el.props.children + const getKey = (el: ReactElement) => el.key + + expect(intersperse([a], comma).map(getText)).toEqual(['a']) + expect(intersperse([a], comma).map(getKey)).toEqual(['a']) + + expect(intersperse([a, b], comma).map(getText)).toEqual(['a', ',', 'b']) + expect(intersperse([a, b], comma).map(getKey)).toEqual(['a', 'sep-1', 'b']) + + expect(intersperse([a, b], comma, or).map(getText)).toEqual(['a', 'or', 'b']) + expect(intersperse([a, b], comma, or).map(getKey)).toEqual(['a', 'conj', 'b']) + + let result = intersperse([a, b, c], comma) + expect(result.map(getText)).toEqual(['a', ',', 'b', ',', 'c']) + expect(result.map(getKey)).toEqual(['a', 'sep-1', 'b', 'sep-2', 'c']) + + result = intersperse([a, b, c], comma, or) + expect(result.map(getText)).toEqual(['a', ',', 'b', ',', 'or', 'c']) + expect(result.map(getKey)).toEqual(['a', 'sep-1', 'b', 'sep-2', 'conj', 'c']) }) diff --git a/libs/util/array.ts b/libs/util/array.ts index fe3b996378..ab5ff348b4 100644 --- a/libs/util/array.ts +++ b/libs/util/array.ts @@ -6,6 +6,8 @@ * Copyright Oxide Computer Company */ +import { cloneElement, type ReactElement } from 'react' + /* eslint-disable @typescript-eslint/no-explicit-any */ const identity = (x: any) => x @@ -80,12 +82,23 @@ export function sumBy(items: T[], fn: (item: T) => number): number { /** * If a conjunction is included, use that instead of `sep` when there are two items. */ -export function intersperse(items: T[], sep: T, conj?: T): T[] { +export function intersperse( + items: ReactElement[], + sep: ReactElement, + conj?: ReactElement +): ReactElement[] { if (items.length <= 1) return items - if (conj && items.length === 2) return [items[0], conj, items[1]] + if (conj && items.length === 2) { + const conj0 = cloneElement(conj, { key: `conj` }) + return [items[0], conj0, items[1]] + } return items.flatMap((item, i) => { if (i === 0) return [item] - if (conj && i === items.length - 1) return [sep, conj, item] - return [sep, item] + const sep0 = cloneElement(sep, { key: `sep-${i}` }) + if (conj && i === items.length - 1) { + const conj0 = cloneElement(conj, { key: `conj` }) + return [sep0, conj0, item] + } + return [sep0, item] }) }