From 1f7c81d9aaefc044e7abb1d09139f920f108d0d5 Mon Sep 17 00:00:00 2001 From: Heath Chiavettone Date: Tue, 17 Aug 2021 11:53:32 -0700 Subject: [PATCH 1/3] In all the bootstrap-4 `TextWidget` wrappers, rather than directly import `TextWidget` from the source hierarchy, instead pull from the `registry.widgets` - These changes allow all of these `TextWidget` wrappers (like `EmailWidget`, etc...) to immediately use a custom `TextWidget` class - Updated the `Registry` type in the `core/index.d.ts` file to add `rootSchema` - This fixed a TODO and avoids the need to cast `registry` to any in the three typescript `ArrayFieldTemplate` implementations - Also updated `WidgetProps` to add an optional `type` - This eliminated the custom `TextWidgetProps` for `bootstrap-4` which prevented a previous PR from converting the `TextWidget` wrappers - Updated all of the `bootstrap-4` widgets to use `TextWidget` from the registry, using simple `WidgetProps` - Updated `TextWidget` to remove `TextWidgetProps` and to simply use `WidgetProps` - Updated the `material-ui` `UpDownWidget` tests to add a blank `rootSchema` to the `registry` - Updated the `bootstrap-4` `createMock()` to add a blank `rootSchema` to the `registry` as well as adding `TextWidget` to the `registry.widgets` --- .../src/ArrayFieldTemplate/ArrayFieldTemplate.tsx | 3 +-- packages/bootstrap-4/src/ColorWidget/ColorWidget.tsx | 6 ++++-- .../bootstrap-4/src/DateTimeWidget/DateTimeWidget.tsx | 9 +++++---- packages/bootstrap-4/src/DateWidget/DateWidget.tsx | 10 ++++++---- packages/bootstrap-4/src/EmailWidget/EmailWidget.tsx | 6 ++++-- packages/bootstrap-4/src/FileWidget/FileWidget.tsx | 6 ++++-- packages/bootstrap-4/src/TextWidget/TextWidget.tsx | 8 ++------ packages/bootstrap-4/src/URLWidget/URLWidget.tsx | 6 ++++-- packages/bootstrap-4/test/helpers/createMocks.ts | 5 ++++- packages/core/index.d.ts | 2 ++ .../src/ArrayFieldTemplate/ArrayFieldTemplate.tsx | 3 +-- .../src/ArrayFieldTemplate/ArrayFieldTemplate.tsx | 3 +-- packages/material-ui/test/UpDownWidget.test.tsx | 1 + 13 files changed, 39 insertions(+), 29 deletions(-) diff --git a/packages/bootstrap-4/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/bootstrap-4/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx index c9e73e8e58..77a4bb705e 100644 --- a/packages/bootstrap-4/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx +++ b/packages/bootstrap-4/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -13,8 +13,7 @@ const { isMultiSelect, getDefaultRegistry } = utils; const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { const { schema, registry = getDefaultRegistry() } = props; - // TODO: update types so we don't have to cast registry as any - if (isMultiSelect(schema, (registry as any).rootSchema)) { + if (isMultiSelect(schema, registry.rootSchema)) { return ; } else { return ; diff --git a/packages/bootstrap-4/src/ColorWidget/ColorWidget.tsx b/packages/bootstrap-4/src/ColorWidget/ColorWidget.tsx index f874dc9ace..ead3906679 100644 --- a/packages/bootstrap-4/src/ColorWidget/ColorWidget.tsx +++ b/packages/bootstrap-4/src/ColorWidget/ColorWidget.tsx @@ -1,7 +1,9 @@ import React from "react"; -import TextWidget, { TextWidgetProps } from "../TextWidget"; +import { WidgetProps } from '@rjsf/core'; -const ColorWidget = (props: TextWidgetProps) => { +const ColorWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; return ; }; diff --git a/packages/bootstrap-4/src/DateTimeWidget/DateTimeWidget.tsx b/packages/bootstrap-4/src/DateTimeWidget/DateTimeWidget.tsx index 06218eeae6..086adf23e8 100644 --- a/packages/bootstrap-4/src/DateTimeWidget/DateTimeWidget.tsx +++ b/packages/bootstrap-4/src/DateTimeWidget/DateTimeWidget.tsx @@ -1,17 +1,18 @@ import React from "react"; -import { utils } from "@rjsf/core"; -import TextWidget, { TextWidgetProps } from "../TextWidget"; +import { utils, WidgetProps } from "@rjsf/core"; const { localToUTC, utcToLocal } = utils; -const DateTimeWidget = (props: TextWidgetProps) => { +const DateTimeWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; const value = utcToLocal(props.value); const onChange = (value: any) => { props.onChange(localToUTC(value)); }; return ( - { +const DateWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; return ( ); }; -export default DateWidget; \ No newline at end of file +export default DateWidget; diff --git a/packages/bootstrap-4/src/EmailWidget/EmailWidget.tsx b/packages/bootstrap-4/src/EmailWidget/EmailWidget.tsx index 3888da5132..372a6b3f93 100644 --- a/packages/bootstrap-4/src/EmailWidget/EmailWidget.tsx +++ b/packages/bootstrap-4/src/EmailWidget/EmailWidget.tsx @@ -1,7 +1,9 @@ import React from "react"; -import TextWidget, { TextWidgetProps } from "../TextWidget"; +import { WidgetProps } from '@rjsf/core'; -const EmailWidget = (props: TextWidgetProps) => { +const EmailWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; return ; }; diff --git a/packages/bootstrap-4/src/FileWidget/FileWidget.tsx b/packages/bootstrap-4/src/FileWidget/FileWidget.tsx index dae207ab93..8ec8ba28fd 100644 --- a/packages/bootstrap-4/src/FileWidget/FileWidget.tsx +++ b/packages/bootstrap-4/src/FileWidget/FileWidget.tsx @@ -1,7 +1,9 @@ import React from "react"; -import TextWidget, { TextWidgetProps } from "../TextWidget"; +import { WidgetProps } from '@rjsf/core'; -const FileWidget = (props: TextWidgetProps) => { +const FileWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; return ; }; diff --git a/packages/bootstrap-4/src/TextWidget/TextWidget.tsx b/packages/bootstrap-4/src/TextWidget/TextWidget.tsx index 3dfe7e968f..a729989ddc 100644 --- a/packages/bootstrap-4/src/TextWidget/TextWidget.tsx +++ b/packages/bootstrap-4/src/TextWidget/TextWidget.tsx @@ -4,10 +4,6 @@ import Form from "react-bootstrap/Form"; import { WidgetProps } from "@rjsf/core"; -export interface TextWidgetProps extends WidgetProps { - type?: string; -} - const TextWidget = ({ id, placeholder, @@ -25,7 +21,7 @@ const TextWidget = ({ schema, rawErrors = [], -}: TextWidgetProps) => { +}: WidgetProps) => { const _onChange = ({ target: { value }, }: React.ChangeEvent) => @@ -36,7 +32,7 @@ const TextWidget = ({ target: { value }, }: React.FocusEvent) => onFocus(id, value); const inputType = (type || schema.type) === 'string' ? 'text' : `${type || schema.type}` - + // const classNames = [rawErrors.length > 0 ? "is-invalid" : "", type === 'file' ? 'custom-file-label': ""] return ( diff --git a/packages/bootstrap-4/src/URLWidget/URLWidget.tsx b/packages/bootstrap-4/src/URLWidget/URLWidget.tsx index c9a3bcd81f..f2ede6bd7d 100644 --- a/packages/bootstrap-4/src/URLWidget/URLWidget.tsx +++ b/packages/bootstrap-4/src/URLWidget/URLWidget.tsx @@ -1,7 +1,9 @@ import React from "react"; -import TextWidget, { TextWidgetProps } from "../TextWidget"; +import { WidgetProps } from '@rjsf/core'; -const URLWidget = (props: TextWidgetProps) => { +const URLWidget = (props: WidgetProps) => { + const { registry } = props; + const { TextWidget } = registry.widgets; return ; }; diff --git a/packages/bootstrap-4/test/helpers/createMocks.ts b/packages/bootstrap-4/test/helpers/createMocks.ts index 8beef122eb..329f676141 100644 --- a/packages/bootstrap-4/test/helpers/createMocks.ts +++ b/packages/bootstrap-4/test/helpers/createMocks.ts @@ -1,6 +1,8 @@ import { WidgetProps } from "@rjsf/core"; import { JSONSchema7 } from "json-schema"; +import TextWidget from "../../src/TextWidget/TextWidget"; + export const mockSchema: JSONSchema7 = { type: "array", items: { @@ -33,9 +35,10 @@ export function makeWidgetMockProps( placeholder: "", registry: { fields: {}, - widgets: {}, + widgets: { TextWidget }, formContext: {}, definitions: {}, + rootSchema: {}, }, ...props, }; diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 08f7b46aaa..33205db7c5 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -115,6 +115,7 @@ declare module '@rjsf/core' { multiple: boolean; rawErrors: string[]; registry: Registry; + type?: string; // Add the optional prop for TextWidget to simplify Typescript usage } export type Widget = React.StatelessComponent | React.ComponentClass; @@ -124,6 +125,7 @@ declare module '@rjsf/core' { widgets: { [name: string]: Widget }; definitions: { [name: string]: any }; formContext: any; + rootSchema: JSONSchema7; } export interface FieldProps diff --git a/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx index fb11d27501..f22d98d8cf 100644 --- a/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx +++ b/packages/fluent-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -16,8 +16,7 @@ const { isMultiSelect, getDefaultRegistry } = utils; const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { const { schema, registry = getDefaultRegistry() } = props; - // TODO: update types so we don't have to cast registry as any - if (isMultiSelect(schema, (registry as any).rootSchema)) { + if (isMultiSelect(schema, registry.rootSchema)) { return ; } else { return ; diff --git a/packages/material-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx b/packages/material-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx index 99f3bc199f..80a4cd3614 100644 --- a/packages/material-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx +++ b/packages/material-ui/src/ArrayFieldTemplate/ArrayFieldTemplate.tsx @@ -19,8 +19,7 @@ const { const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { const { schema, registry = getDefaultRegistry() } = props; - // TODO: update types so we don't have to cast registry as any - if (isMultiSelect(schema, (registry as any).rootSchema)) { + if (isMultiSelect(schema, registry.rootSchema)) { return ; } else { return ; diff --git a/packages/material-ui/test/UpDownWidget.test.tsx b/packages/material-ui/test/UpDownWidget.test.tsx index 46c5f4c129..82842da61d 100644 --- a/packages/material-ui/test/UpDownWidget.test.tsx +++ b/packages/material-ui/test/UpDownWidget.test.tsx @@ -38,6 +38,7 @@ describe("UpDownWidget", () => { widgets: {}, definitions: {}, formContext: {}, + rootSchema: {}, } }; const tree = renderer.create().toJSON(); From 8cf72b87793774bf266e428c19a75c2e7f17f125 Mon Sep 17 00:00:00 2001 From: Heath C <51679588+heath-freenome@users.noreply.github.com> Date: Tue, 17 Aug 2021 13:24:40 -0700 Subject: [PATCH 2/3] Update index.d.ts - Made the `FieldProps.onFocus` match the definition of `WidgetProps.onFocus` to avoid type conversion error when creating custom components --- packages/core/index.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 33205db7c5..318260b871 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -129,7 +129,7 @@ declare module '@rjsf/core' { } export interface FieldProps - extends Pick, Exclude, 'onBlur'>> { + extends Pick, Exclude, 'onBlur' | 'onFocus'>> { schema: JSONSchema7; uiSchema: UiSchema; idSchema: IdSchema; @@ -137,6 +137,7 @@ declare module '@rjsf/core' { errorSchema: ErrorSchema; onChange: (e: IChangeEvent | any, es?: ErrorSchema) => any; onBlur: (id: string, value: any) => void; + onFocus: (id: string, value: any) => void; registry: Registry; formContext: any; autofocus: boolean; From d1379b81d2f85c377a6000228cb63f7e3616d1fb Mon Sep 17 00:00:00 2001 From: Heath C <51679588+heath-freenome@users.noreply.github.com> Date: Wed, 18 Aug 2021 13:17:13 -0700 Subject: [PATCH 3/3] Update index.d.ts --- packages/core/index.d.ts | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 318260b871..1267e88d3f 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -115,17 +115,17 @@ declare module '@rjsf/core' { multiple: boolean; rawErrors: string[]; registry: Registry; - type?: string; // Add the optional prop for TextWidget to simplify Typescript usage + [prop: string]: any; // Allow for other props } export type Widget = React.StatelessComponent | React.ComponentClass; export interface Registry { - fields: { [name: string]: Field }; - widgets: { [name: string]: Widget }; - definitions: { [name: string]: any }; - formContext: any; - rootSchema: JSONSchema7; + fields: { [name: string]: Field }; + widgets: { [name: string]: Widget }; + definitions: { [name: string]: any }; + formContext: any; + rootSchema: JSONSchema7; } export interface FieldProps @@ -145,7 +145,7 @@ declare module '@rjsf/core' { readonly: boolean; required: boolean; name: string; - [prop: string]: any; + [prop: string]: any; // Allow for other props } export type Field = React.StatelessComponent | React.ComponentClass; @@ -174,7 +174,7 @@ declare module '@rjsf/core' { onChange: (value: T) => void; onKeyChange: (value: string) => () => void; onDropPropertyClick: (value: string) => () => void; - registry: FieldProps['registry']; + registry: Registry; }; export type ArrayFieldTemplateProps = { @@ -207,7 +207,7 @@ declare module '@rjsf/core' { title: string; formContext: any; formData: T; - registry: FieldProps['registry']; + registry: Registry; }; export type ObjectFieldTemplateProps = { @@ -231,7 +231,7 @@ declare module '@rjsf/core' { idSchema: IdSchema; formData: T; formContext: any; - registry: FieldProps['registry']; + registry: Registry; }; export type AjvError = { @@ -296,7 +296,7 @@ declare module '@rjsf/core' { export function canExpand(schema: JSONSchema7, uiSchema: UiSchema, formData: any): boolean; - export function getDefaultRegistry(): FieldProps['registry']; + export function getDefaultRegistry(): Registry; export function getSchemaType(schema: JSONSchema7): string; @@ -315,7 +315,7 @@ declare module '@rjsf/core' { export function computeDefaults( schema: JSONSchema7, parentDefaults: JSONSchema7['default'][], - definitions: FieldProps['registry']['definitions'], + definitions: Registry['definitions'], rawFormData?: T, includeUndefinedValues?: boolean, ): JSONSchema7['default'][]; @@ -323,7 +323,7 @@ declare module '@rjsf/core' { export function getDefaultFormState( schema: JSONSchema7, formData: T, - definitions?: FieldProps['registry']['definitions'], + definitions?: Registry['definitions'], includeUndefinedValues?: boolean, ): T | JSONSchema7['default'][]; @@ -343,14 +343,14 @@ declare module '@rjsf/core' { export function toConstant(schema: JSONSchema7): JSONSchema7Type | JSONSchema7['const']; - export function isSelect(_schema: JSONSchema7, definitions?: FieldProps['registry']['definitions']): boolean; + export function isSelect(_schema: JSONSchema7, definitions?: Registry['definitions']): boolean; - export function isMultiSelect(schema: JSONSchema7, definitions?: FieldProps['registry']['definitions']): boolean; + export function isMultiSelect(schema: JSONSchema7, definitions?: Registry['definitions']): boolean; export function isFilesArray( schema: JSONSchema7, uiSchema: UiSchema, - definitions?: FieldProps['registry']['definitions'], + definitions?: Registry['definitions'], ): boolean; export function isFixedItems(schema: JSONSchema7): boolean; @@ -367,19 +367,19 @@ declare module '@rjsf/core' { export function stubExistingAdditionalProperties( schema: JSONSchema7, - definitions?: FieldProps['registry']['definitions'], + definitions?: Registry['definitions'], formData?: T, ): JSONSchema7; export function resolveSchema( schema: JSONSchema7Definition, - definitions?: FieldProps['registry']['definitions'], + definitions?: Registry['definitions'], formData?: T, ): JSONSchema7; export function retrieveSchema( schema: JSONSchema7Definition, - definitions?: FieldProps['registry']['definitions'], + definitions?: Registry['definitions'], formData?: T, ): JSONSchema7; @@ -390,7 +390,7 @@ declare module '@rjsf/core' { export function toIdSchema( schema: JSONSchema7Definition, id: string, - definitions: FieldProps['registry']['definitions'], + definitions: Registry['definitions'], formData?: T, idPredix?: string, ): IdSchema | IdSchema[]; @@ -398,7 +398,7 @@ declare module '@rjsf/core' { export function toPathSchema( schema: JSONSchema7Definition, name: string | undefined, - definitions: FieldProps['registry']['definitions'], + definitions: Registry['definitions'], formData?: T, ): PathSchema | PathSchema[]; @@ -436,7 +436,7 @@ declare module '@rjsf/core' { export function getMatchingOption( formData: any, options: JSONSchema7[], - definitions: FieldProps['registry']['definitions'], + definitions: Registry['definitions'], ): number; export function schemaRequiresTrueValue(schema: JSONSchema7): boolean;