From 4d1bb3eb38eef865c271a96c3891264626258916 Mon Sep 17 00:00:00 2001 From: Jacob Mischka Date: Wed, 19 Oct 2022 13:24:23 -0500 Subject: [PATCH 1/4] Split DISPLAY_SCHEMA and INPUT_SCHEMA, rejoin for ioSchema Removes the need to explicitly list the Display keys afterward, generally cleaner. --- src/ioSchema.ts | 257 +++++++++++++++++++++++------------------------- 1 file changed, 125 insertions(+), 132 deletions(-) diff --git a/src/ioSchema.ts b/src/ioSchema.ts index f9880a1..878a305 100644 --- a/src/ioSchema.ts +++ b/src/ioSchema.ts @@ -293,10 +293,124 @@ export function resolvesImmediately(methodName: T_IO_METHOD_NAMES): boolean { return 'immediate' in ioSchema[methodName] } -/** - * IMPORTANT: When adding any new DISPLAY methods, be sure to also add their method names to T_IO_DISPLAY_METHOD_NAMES below. - */ -export const ioSchema = { +const DISPLAY_SCHEMA = { + DISPLAY_CODE: { + props: z.object({ + code: z.string(), + language: z.string().optional(), + }), + state: z.null(), + returns: z.null(), + }, + DISPLAY_HEADING: { + props: z.object({}), + state: z.null(), + returns: z.null(), + }, + DISPLAY_MARKDOWN: { + props: z.object({}), + state: z.null(), + returns: z.null(), + }, + DISPLAY_IMAGE: { + props: z.object({ + alt: z.string().optional(), + width: imageSize.optional(), + height: imageSize.optional(), + url: z.string(), + }), + state: z.null(), + returns: z.null(), + }, + DISPLAY_LINK: { + props: z.intersection( + z.object({ + theme: buttonTheme, + }), + z.union([ + z.object({ + href: z.string(), + }), + linkSchema, + ]) + ), + state: z.null(), + returns: z.null(), + }, + DISPLAY_OBJECT: { + props: z.object({ + data: keyValueObject, + }), + state: z.null(), + returns: z.null(), + }, + DISPLAY_TABLE: { + props: z.object({ + helpText: z.optional(z.string()), + columns: z.array(internalTableColumn), + data: z.array(internalTableRow), + orientation: z.enum(['vertical', 'horizontal']).default('horizontal'), + defaultPageSize: z.number().optional(), + //== private props + // added in v0.28, optional until required by all active versions + totalRecords: z.optional(z.number().int()), + isAsync: z.optional(z.boolean().default(false)), + }), + state: z.object({ + queryTerm: z.string().optional(), + sortColumn: z.string().optional(), + sortDirection: z.enum(['asc', 'desc']).optional(), + offset: z.number().int().default(0), + pageSize: z.number().int(), + }), + returns: z.null(), + }, + DISPLAY_PROGRESS_STEPS: { + props: z.object({ + steps: z.object({ + completed: z.number(), + total: z.number(), + }), + currentStep: z.string().optional(), + subTitle: z.string().optional(), + }), + state: z.null(), + returns: z.null(), + immediate: z.literal(true), + }, + DISPLAY_PROGRESS_INDETERMINATE: { + props: z.object({}), + state: z.null(), + returns: z.null(), + immediate: z.literal(true), + }, + DISPLAY_PROGRESS_THROUGH_LIST: { + props: z.object({ + items: z.array( + z.object({ + label: z.string(), + isComplete: z.boolean(), + resultDescription: z.union([z.null(), z.string()]), + }) + ), + }), + state: z.null(), + returns: z.null(), + }, + DISPLAY_VIDEO: { + props: z.object({ + width: imageSize.optional(), + height: imageSize.optional(), + url: z.string(), + loop: z.boolean().optional(), + muted: z.boolean().optional(), + }), + state: z.null(), + returns: z.null(), + }, +} + +const INPUT_SCHEMA = { INPUT_TEXT: { props: z.object({ helpText: z.optional(z.string()), @@ -514,120 +628,11 @@ export const ioSchema = { state: z.null(), returns: z.array(labelValue), }, - DISPLAY_CODE: { - props: z.object({ - code: z.string(), - language: z.string().optional(), - }), - state: z.null(), - returns: z.null(), - }, - DISPLAY_HEADING: { - props: z.object({}), - state: z.null(), - returns: z.null(), - }, - DISPLAY_MARKDOWN: { - props: z.object({}), - state: z.null(), - returns: z.null(), - }, - DISPLAY_IMAGE: { - props: z.object({ - alt: z.string().optional(), - width: imageSize.optional(), - height: imageSize.optional(), - url: z.string(), - }), - state: z.null(), - returns: z.null(), - }, - DISPLAY_LINK: { - props: z.intersection( - z.object({ - theme: buttonTheme, - }), - z.union([ - z.object({ - href: z.string(), - }), - linkSchema, - ]) - ), - state: z.null(), - returns: z.null(), - }, - DISPLAY_OBJECT: { - props: z.object({ - data: keyValueObject, - }), - state: z.null(), - returns: z.null(), - }, - DISPLAY_TABLE: { - props: z.object({ - helpText: z.optional(z.string()), - columns: z.array(internalTableColumn), - data: z.array(internalTableRow), - orientation: z.enum(['vertical', 'horizontal']).default('horizontal'), - defaultPageSize: z.number().optional(), - //== private props - // added in v0.28, optional until required by all active versions - totalRecords: z.optional(z.number().int()), - isAsync: z.optional(z.boolean().default(false)), - }), - state: z.object({ - queryTerm: z.string().optional(), - sortColumn: z.string().optional(), - sortDirection: z.enum(['asc', 'desc']).optional(), - offset: z.number().int().default(0), - pageSize: z.number().int(), - }), - returns: z.null(), - }, - DISPLAY_PROGRESS_STEPS: { - props: z.object({ - steps: z.object({ - completed: z.number(), - total: z.number(), - }), - currentStep: z.string().optional(), - subTitle: z.string().optional(), - }), - state: z.null(), - returns: z.null(), - immediate: z.literal(true), - }, - DISPLAY_PROGRESS_INDETERMINATE: { - props: z.object({}), - state: z.null(), - returns: z.null(), - immediate: z.literal(true), - }, - DISPLAY_PROGRESS_THROUGH_LIST: { - props: z.object({ - items: z.array( - z.object({ - label: z.string(), - isComplete: z.boolean(), - resultDescription: z.union([z.null(), z.string()]), - }) - ), - }), - state: z.null(), - returns: z.null(), - }, - DISPLAY_VIDEO: { - props: z.object({ - width: imageSize.optional(), - height: imageSize.optional(), - url: z.string(), - loop: z.boolean().optional(), - muted: z.boolean().optional(), - }), - state: z.null(), - returns: z.null(), - }, +} + +export const ioSchema = { + ...DISPLAY_SCHEMA, + ...INPUT_SCHEMA, } export type IoMethod = { @@ -639,20 +644,8 @@ export type IoMethod = { export type T_IO_Schema = typeof ioSchema export type T_IO_METHOD_NAMES = keyof T_IO_Schema -export type T_IO_DISPLAY_METHOD_NAMES = - | 'DISPLAY_CODE' - | 'DISPLAY_HEADING' - | 'DISPLAY_MARKDOWN' - | 'DISPLAY_LINK' - | 'DISPLAY_OBJECT' - | 'DISPLAY_TABLE' - | 'DISPLAY_IMAGE' - | 'DISPLAY_VIDEO' - -export type T_IO_INPUT_METHOD_NAMES = Exclude< - T_IO_METHOD_NAMES, - T_IO_DISPLAY_METHOD_NAMES -> +export type T_IO_DISPLAY_METHOD_NAMES = keyof typeof DISPLAY_SCHEMA +export type T_IO_INPUT_METHOD_NAMES = keyof typeof INPUT_SCHEMA type T_Fields = 'props' | 'state' | 'returns' From 74c4919142eb956a6cbaa5bd7876d5ffa6801676 Mon Sep 17 00:00:00 2001 From: Jacob Mischka Date: Wed, 19 Oct 2022 15:05:28 -0500 Subject: [PATCH 2/4] Add description, level, and menuItems props to io.display.heading Close T-215 --- src/classes/Layout.ts | 8 +------- src/examples/basic/index.ts | 21 +++++++++++++++++++++ src/ioSchema.ts | 6 +++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/classes/Layout.ts b/src/classes/Layout.ts index 78ae2d2..2325e43 100644 --- a/src/classes/Layout.ts +++ b/src/classes/Layout.ts @@ -1,11 +1,5 @@ import { z } from 'zod' -import { - primitiveValue, - Literal, - IO_RENDER, - menuItem, - buttonItem, -} from '../ioSchema' +import { primitiveValue, Literal, IO_RENDER, buttonItem } from '../ioSchema' import { AnyDisplayIOPromise, ButtonItem, PageError } from '../types' type EventualString = diff --git a/src/examples/basic/index.ts b/src/examples/basic/index.ts index 901aad4..2ae66a5 100644 --- a/src/examples/basic/index.ts +++ b/src/examples/basic/index.ts @@ -156,6 +156,27 @@ const interval = new Interval({ logLevel: 'debug', endpoint: 'ws://localhost:3000/websocket', routes: { + section_heading: async io => { + await io.group([ + io.display.heading('Section heading', { + level: 2, + description: 'A section heading here', + menuItems: [ + { label: 'Link', url: 'https://interval.com', theme: 'primary' }, + { label: 'Danger', action: 'disabled_inputs', theme: 'danger' }, + ], + }), + io.input.text('Text input'), + io.input.text('Multiline', { + multiline: true, + }), + + io.display.heading('Sub-heading', { + level: 3, + description: 'A subsection', + }), + ]) + }, disabled_inputs: async io => { await io.group([ io.display.heading('Here are a bunch of disabled inputs'), diff --git a/src/ioSchema.ts b/src/ioSchema.ts index 878a305..59b0493 100644 --- a/src/ioSchema.ts +++ b/src/ioSchema.ts @@ -303,7 +303,11 @@ const DISPLAY_SCHEMA = { returns: z.null(), }, DISPLAY_HEADING: { - props: z.object({}), + props: z.object({ + level: z.union([z.literal(2), z.literal(3), z.literal(4)]).optional(), + description: z.string().optional(), + menuItems: z.array(buttonItem).optional(), + }), state: z.null(), returns: z.null(), }, From 2e746ebcf12e9f33d97b440d7dac1553dc59109a Mon Sep 17 00:00:00 2001 From: Dan Philibin Date: Wed, 19 Oct 2022 21:35:06 +0000 Subject: [PATCH 3/4] add heading to Layout.Basic children in the `app` example --- src/examples/app/index.ts | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/examples/app/index.ts b/src/examples/app/index.ts index 2613cbf..244b34a 100644 --- a/src/examples/app/index.ts +++ b/src/examples/app/index.ts @@ -57,10 +57,29 @@ const hello_app = new Router({ label: 'Hello', action: 'hello_app/hello_world', }, - // { - // label: 'Inline', - // action: async () => {}, - // }, + ], + }), + io.display.heading('Section two', { + level: 3, + description: 'This table has the same data as the previous one.', + menuItems: [ + { + label: 'Action link', + action: 'hello_app/hello_world', + }, + ], + }), + io.display.table('', { + data: [ + { a: 1, b: 2, c: 3 }, + { a: 1, b: 2, c: 3 }, + { a: 1, b: 2, c: 3 }, + ], + rowMenuItems: () => [ + { + label: 'Hello', + action: 'hello_app/hello_world', + }, ], }), ], From 3799dc4a39b70a3a4f0739f6bc828c07d74c7b46 Mon Sep 17 00:00:00 2001 From: Dan Philibin Date: Wed, 19 Oct 2022 21:41:57 +0000 Subject: [PATCH 4/4] remove default modifier for buttonTheme removed for clarity - it didn't seem to do anything since the prop is optional everywhere that we use it. I also thought `optional` should be chained to the references and not the original value, so I moved that, too. --- src/ioSchema.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ioSchema.ts b/src/ioSchema.ts index 59b0493..5b93970 100644 --- a/src/ioSchema.ts +++ b/src/ioSchema.ts @@ -30,10 +30,7 @@ export const DISPLAY_RENDER = z.object({ }) // `default` deprecated in 0.31.0 -const buttonTheme = z - .enum(['default', 'primary', 'secondary', 'danger']) - .default('primary') - .optional() +const buttonTheme = z.enum(['default', 'primary', 'secondary', 'danger']) export type ButtonTheme = 'primary' | 'secondary' | 'danger' export const IO_RENDER = z.object({ @@ -44,7 +41,7 @@ export const IO_RENDER = z.object({ continueButton: z .object({ label: z.string().optional(), - theme: buttonTheme, + theme: buttonTheme.optional(), }) .optional(), kind: z.literal('RENDER'), @@ -214,7 +211,7 @@ export const menuItem = z.intersection( export const buttonItem = z.intersection( z.object({ label: z.string(), - theme: buttonTheme, + theme: buttonTheme.optional(), }), z.union([ z.object({ @@ -329,7 +326,7 @@ const DISPLAY_SCHEMA = { DISPLAY_LINK: { props: z.intersection( z.object({ - theme: buttonTheme, + theme: buttonTheme.optional(), }), z.union([ z.object({