Skip to content

Commit

Permalink
Also allow booleans and dates as labels/values, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobmischka committed Jul 20, 2022
1 parent 7207de6 commit 76d52c3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 70 deletions.
4 changes: 3 additions & 1 deletion src/components/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import IOError from '../classes/IOError'
type RenderResultDef =
| string
| number
| boolean
| Date
| {
label: string | number
label: string | number | boolean | Date
description?: string
imageUrl?: string
}
Expand Down
67 changes: 38 additions & 29 deletions src/components/selectMultiple.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,55 @@
import { z } from 'zod'
import { T_IO_PROPS, T_IO_RETURNS, labelValue } from '../ioSchema'
import {
T_IO_PROPS,
T_IO_RETURNS,
labelValue,
primitiveValue,
} from '../ioSchema'
import Logger from '../classes/Logger'

type SelectMultipleProps<Option extends z.infer<typeof labelValue> | string> =
Omit<T_IO_PROPS<'SELECT_MULTIPLE'>, 'options' | 'defaultValue'> & {
options: Option[]
defaultValue?: Option[]
}
type SelectMultipleProps<
Option extends z.infer<typeof labelValue> | z.infer<typeof primitiveValue>
> = Omit<T_IO_PROPS<'SELECT_MULTIPLE'>, 'options' | 'defaultValue'> & {
options: Option[]
defaultValue?: Option[]
}

export default function selectMultiple(logger: Logger) {
return <Option extends z.infer<typeof labelValue> | string>(
return <
Option extends z.infer<typeof labelValue> | z.infer<typeof primitiveValue>
>(
props: SelectMultipleProps<Option>
) => {
const normalizedOptions: z.infer<typeof labelValue>[] = props.options.map(
option => {
if (typeof option === 'string') {
return {
label: option,
value: option,
}
} else {
return option as Exclude<Option, string>
function normalizeOption(option: Option) {
if (
typeof option === 'string' ||
typeof option === 'number' ||
typeof option === 'boolean' ||
option instanceof Date
) {
return {
label: option,
value: option,
}
} else {
return option as Exclude<Option, string | number | boolean | Date>
}
}

const normalizedOptions: z.infer<typeof labelValue>[] = props.options.map(
option => normalizeOption(option)
)
type Options = typeof props.options
const optionMap = new Map(
normalizedOptions.map((o, i) => [o.value, props.options[i]])
normalizedOptions.map((o, i) => [o.value.toString(), props.options[i]])
)

let defaultValue = props.defaultValue?.map(d => {
if (typeof d === 'string') {
return {
label: d,
value: d,
}
} else {
return d as Exclude<Option, string>
}
})
let defaultValue = props.defaultValue?.map(d => normalizeOption(d))

if (defaultValue && defaultValue.every(val => !optionMap.has(val.value))) {
if (
defaultValue &&
defaultValue.every(val => !optionMap.has(val.value.toString()))
) {
logger.warn(
'The defaultValue property must be a subset of the provided options, the provided defaultValue will be discarded.'
)
Expand All @@ -56,7 +65,7 @@ export default function selectMultiple(logger: Logger) {
options: normalizedOptions.map(o => stripper.parse(o)),
},
getValue(response: T_IO_RETURNS<'SELECT_MULTIPLE'>): Options {
return response.map(r => optionMap.get(r.value)) as Options
return response.map(r => optionMap.get(r.value.toString())) as Options
},
}
}
Expand Down
50 changes: 33 additions & 17 deletions src/components/selectSingle.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,60 @@
import { z } from 'zod'
import { T_IO_PROPS, T_IO_RETURNS, richSelectOption } from '../ioSchema'
import {
T_IO_PROPS,
T_IO_RETURNS,
richSelectOption,
primitiveValue,
} from '../ioSchema'
import Logger from '../classes/Logger'

type SelectSingleProps<
Option extends z.infer<typeof richSelectOption> | string
Option extends
| z.infer<typeof richSelectOption>
| z.infer<typeof primitiveValue>
> = Omit<T_IO_PROPS<'SELECT_SINGLE'>, 'options' | 'defaultValue'> & {
options: Option[]
defaultValue?: Option
}

export default function selectSingle(logger: Logger) {
return <Option extends z.infer<typeof richSelectOption> | string>(
return <
Option extends
| z.infer<typeof richSelectOption>
| z.infer<typeof primitiveValue>
>(
props: SelectSingleProps<Option>
) => {
const normalizedOptions = props.options.map(option => {
if (typeof option === 'string') {
function normalizeOption(option: Option) {
if (
typeof option === 'string' ||
typeof option === 'number' ||
typeof option === 'boolean' ||
option instanceof Date
) {
return {
label: option,
value: option,
}
} else {
return option as Exclude<Option, string>
return option as Exclude<Option, string | number | boolean | Date>
}
})
}

const normalizedOptions = props.options.map(option =>
normalizeOption(option)
)

type Options = typeof props.options
const optionMap = new Map(
normalizedOptions.map((o, i) => [o.value, props.options[i]])
normalizedOptions.map((o, i) => [o.value.toString(), props.options[i]])
)
const stripper = richSelectOption.strip()

let defaultValue =
typeof props.defaultValue === 'string'
? {
label: props.defaultValue,
value: props.defaultValue,
}
: (props.defaultValue as Exclude<Option, string> | undefined)
let defaultValue = props.defaultValue
? normalizeOption(props.defaultValue)
: undefined

if (defaultValue && !optionMap.has(defaultValue.value)) {
if (defaultValue && !optionMap.has(defaultValue.value.toString())) {
logger.warn(
'The defaultValue property must be a value in the provided options, the provided defaultValue will be discarded.'
)
Expand All @@ -52,7 +68,7 @@ export default function selectSingle(logger: Logger) {
options: normalizedOptions.map(o => stripper.parse(o)),
},
getValue(response: T_IO_RETURNS<'SELECT_SINGLE'>) {
return optionMap.get(response.value) as Options[0]
return optionMap.get(response.value.toString()) as Options[0]
},
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/examples/basic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,17 +377,17 @@ const interval = new Interval({
const options = [
{
value: 0,
label: 'A',
label: 0,
extraData: 'A',
},
{
value: 1,
label: 'B',
value: new Date(2022, 6, 1),
label: new Date(2022, 6, 1),
extraData: 'B',
},
{
value: 2,
label: 'C',
value: true,
label: true,
extraData: 'C',
},
]
Expand Down
36 changes: 18 additions & 18 deletions src/ioSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,39 +46,39 @@ export const typeValue = z.enum([
])
export type TypeValue = z.infer<typeof typeValue>

export const primitiveValue = z.union([
z.string(),
z.number(),
z.boolean(),
z.date(),
])

const objectLiteralSchema = primitiveValue.nullish()

type Literal = boolean | null | number | string | Date | undefined
// `object` is workaround for https://github.com/microsoft/TypeScript/issues/15300
type KeyValue = Literal | { [key: string]: KeyValue } | KeyValue[] | object

export const labelValue = z
.object({
label: z.union([z.string(), z.number()]),
value: z.union([z.string(), z.number()]),
label: primitiveValue,
value: primitiveValue,
})
.passthrough()

export type LabelValue = z.infer<typeof labelValue>

export const richSelectOption = z
.object({
label: z.union([z.string(), z.number()]),
value: z.union([z.string(), z.number()]),
label: primitiveValue,
value: primitiveValue,
description: z.string().nullish(),
imageUrl: z.string().nullish(),
})
.passthrough()

export type RichSelectOption = z.infer<typeof richSelectOption>

const objectLiteralSchema = z.union([
z.string(),
z.number(),
z.boolean(),
z.null(),
z.date(),
z.undefined(),
])

type Literal = boolean | null | number | string | Date | undefined
// `object` is workaround for https://github.com/microsoft/TypeScript/issues/15300
type KeyValue = Literal | { [key: string]: KeyValue } | KeyValue[] | object

const keyValueObject: z.ZodSchema<KeyValue> = z.lazy(() =>
z.union([
objectLiteralSchema,
Expand Down Expand Up @@ -352,7 +352,7 @@ export const ioSchema = {
results: z.array(
z.object({
value: z.string(),
label: z.union([z.string(), z.number()]),
label: primitiveValue,
description: z.string().nullish(),
imageUrl: z.string().nullish(),
})
Expand Down

0 comments on commit 76d52c3

Please sign in to comment.