diff --git a/packages/toolpad-app/src/runtime/ToolpadApp.tsx b/packages/toolpad-app/src/runtime/ToolpadApp.tsx index a2c17f56c50..ca541b285f7 100644 --- a/packages/toolpad-app/src/runtime/ToolpadApp.tsx +++ b/packages/toolpad-app/src/runtime/ToolpadApp.tsx @@ -23,6 +23,7 @@ import { NestedBindableAttrs, GlobalScopeMeta, BindingEvaluationResult, + ArgTypeDefinition, } from '@mui/toolpad-core'; import { createProvidedContext } from '@mui/toolpad-core/utils/react'; import { QueryClient, QueryClientProvider, useMutation } from '@tanstack/react-query'; @@ -77,6 +78,10 @@ import { bridge } from '../canvas/ToolpadBridge'; import Header from '../toolpad/ToolpadShell/Header'; import { ThemeProvider } from '../ThemeContext'; +export function getArgTypeDefaultValue(argType: ArgTypeDefinition<{}, V>): V | undefined { + return argType.typeDef.default ?? argType.defaultValue ?? undefined; +} + const ReactQueryDevtoolsProduction = React.lazy(() => import('@tanstack/react-query-devtools/build/lib/index.prod.js').then((d) => ({ default: d.ReactQueryDevtools, @@ -226,7 +231,7 @@ function RenderedNodeContent({ node, childNodeGroups, Component }: RenderedNodeC } if (typeof hookResult[propName] === 'undefined' && argType) { - hookResult[propName] = argType.defaultValue; + hookResult[propName] = getArgTypeDefaultValue(argType); } } @@ -256,7 +261,7 @@ function RenderedNodeContent({ node, childNodeGroups, Component }: RenderedNodeC } if (typeof hookResult[propName] === 'undefined' && argType) { - hookResult[propName] = argType.defaultValue; + hookResult[propName] = getArgTypeDefaultValue(argType); } } @@ -641,7 +646,8 @@ function parseBindings( : undefined; const binding: BindableAttrValue = - elm.props?.[propName] || appDom.createConst(argType?.defaultValue ?? undefined); + elm.props?.[propName] || + appDom.createConst(argType ? getArgTypeDefaultValue(argType) : undefined); const bindingId = `${elm.id}.props.${propName}`; @@ -671,7 +677,7 @@ function parseBindings( for (const [propName, argType] of Object.entries(layoutBoxArgTypes)) { const binding = elm.layout?.[propName as keyof typeof layoutBoxArgTypes] || - appDom.createConst(argType?.defaultValue ?? undefined); + appDom.createConst(argType ? getArgTypeDefaultValue(argType) : undefined); const bindingId = `${elm.id}.layout.${propName}`; parsedBindingsMap.set(bindingId, parseBinding(binding, {})); } diff --git a/packages/toolpad-app/src/toolpad/AppEditor/CodeComponentEditor/index.tsx b/packages/toolpad-app/src/toolpad/AppEditor/CodeComponentEditor/index.tsx index 73fda490107..5072611fa97 100644 --- a/packages/toolpad-app/src/toolpad/AppEditor/CodeComponentEditor/index.tsx +++ b/packages/toolpad-app/src/toolpad/AppEditor/CodeComponentEditor/index.tsx @@ -36,6 +36,7 @@ import { useNodeNameValidation } from '../HierarchyExplorer/validation'; import useUndoRedo from '../../hooks/useUndoRedo'; import config from '../../../config'; import client from '../../../api'; +import { getArgTypeDefaultValue } from '../../../runtime'; const TypescriptEditor = lazyComponent(() => import('../../../components/TypescriptEditor'), { noSsr: true, @@ -190,7 +191,7 @@ function CodeComponentEditorContent({ codeComponentNode }: CodeComponentEditorCo const { argTypes = {} } = CodeComponent[TOOLPAD_COMPONENT]; const defaultProps = React.useMemo( - () => mapValues(argTypes, (argType) => argType?.defaultValue), + () => mapValues(argTypes, (argType) => (argType ? getArgTypeDefaultValue(argType) : undefined)), [argTypes], ); diff --git a/packages/toolpad-app/src/toolpad/AppEditor/HierarchyExplorer/CreateCodeComponentNodeDialog.tsx b/packages/toolpad-app/src/toolpad/AppEditor/HierarchyExplorer/CreateCodeComponentNodeDialog.tsx index cc78b9ff5f5..1336faa741a 100644 --- a/packages/toolpad-app/src/toolpad/AppEditor/HierarchyExplorer/CreateCodeComponentNodeDialog.tsx +++ b/packages/toolpad-app/src/toolpad/AppEditor/HierarchyExplorer/CreateCodeComponentNodeDialog.tsx @@ -38,8 +38,7 @@ function createDefaultCodeComponent(name: string): string { export default createComponent(${componentId}, { argTypes: { msg: { - typeDef: { type: "string" }, - defaultValue: "Hello world!", + typeDef: { type: "string", default: "Hello world!" }, }, }, }); diff --git a/packages/toolpad-app/src/toolpad/propertyControls/select.tsx b/packages/toolpad-app/src/toolpad/propertyControls/select.tsx index 1cabe20ed44..dbba0fad24b 100644 --- a/packages/toolpad-app/src/toolpad/propertyControls/select.tsx +++ b/packages/toolpad-app/src/toolpad/propertyControls/select.tsx @@ -20,7 +20,7 @@ function SelectPropEditor({ label, propType, value, onChange, disabled }: Editor disabled={disabled} onChange={handleChange} > - - + {typeof propType.default === 'undefined' ? - : null} {items.map((item) => ( {item} diff --git a/packages/toolpad-app/src/toolpadComponents/layoutBox.ts b/packages/toolpad-app/src/toolpadComponents/layoutBox.ts index 4941bc82e52..345cf90591d 100644 --- a/packages/toolpad-app/src/toolpadComponents/layoutBox.ts +++ b/packages/toolpad-app/src/toolpadComponents/layoutBox.ts @@ -13,18 +13,18 @@ export const layoutBoxArgTypes: { typeDef: { type: 'string', enum: ['start', 'center', 'end', 'space-between', 'space-around', 'space-evenly'], + default: 'start', }, label: 'Horizontal alignment', control: { type: 'HorizontalAlign' }, - defaultValue: 'start', }, verticalAlign: { typeDef: { type: 'string', enum: ['start', 'center', 'end', 'space-between', 'space-around', 'space-evenly'], + default: 'center', }, label: 'Vertical alignment', control: { type: 'VerticalAlign' }, - defaultValue: 'center', }, }; diff --git a/packages/toolpad-components/src/Button.tsx b/packages/toolpad-components/src/Button.tsx index dcc62982d7c..3dbb0712047 100644 --- a/packages/toolpad-components/src/Button.tsx +++ b/packages/toolpad-components/src/Button.tsx @@ -22,23 +22,24 @@ export default createComponent(Button, { }, content: { helperText: 'Will appear as the text content of the button.', - typeDef: { type: 'string' }, - defaultValue: 'Button Text', + typeDef: { type: 'string', default: 'Button Text' }, }, variant: { helperText: 'One of the available MUI Button [variants](https://mui.com/material-ui/react-button/#basic-button). Possible values are `contained`, `outlined` or `text`', - typeDef: { type: 'string', enum: ['contained', 'outlined', 'text'] }, - defaultValue: 'contained', + typeDef: { + type: 'string', + enum: ['contained', 'outlined', 'text'], + default: 'contained', + }, }, size: { helperText: 'The size of the component. One of `small`, `medium`, or `large`.', - typeDef: { type: 'string', enum: ['small', 'medium', 'large'] }, + typeDef: { type: 'string', enum: ['small', 'medium', 'large'], default: 'small' }, }, color: { helperText: 'The theme color of the component.', - typeDef: { type: 'string', enum: ['primary', 'secondary'] }, - defaultValue: 'primary', + typeDef: { type: 'string', enum: ['primary', 'secondary'], default: 'primary' }, }, fullWidth: { helperText: 'Whether the button should occupy all available horizontal space.', diff --git a/packages/toolpad-components/src/Container.tsx b/packages/toolpad-components/src/Container.tsx index 2668dd0b21c..5507a731ba2 100644 --- a/packages/toolpad-components/src/Container.tsx +++ b/packages/toolpad-components/src/Container.tsx @@ -22,14 +22,12 @@ export default createComponent(Container, { control: { type: 'layoutSlot' }, }, visible: { - typeDef: { type: 'boolean' }, - defaultValue: true, + typeDef: { type: 'boolean', default: true }, helperText: 'Control whether container element is visible.', }, sx: { helperText: SX_PROP_HELPER_TEXT, - typeDef: { type: 'object' }, - defaultValue: { padding: 1, border: 'solid 1px' }, + typeDef: { type: 'object', default: { padding: 1, border: 'solid 1px' } }, }, }, }); diff --git a/packages/toolpad-components/src/DataGrid.tsx b/packages/toolpad-components/src/DataGrid.tsx index d3b1eba79ff..99af56cff37 100644 --- a/packages/toolpad-components/src/DataGrid.tsx +++ b/packages/toolpad-components/src/DataGrid.tsx @@ -520,19 +520,16 @@ export default createComponent(DataGridComponent, { }, selection: { helperText: 'The currently selected row. Or `null` in case no row has been selected.', - typeDef: { type: 'object' }, + typeDef: { type: 'object', default: null }, onChangeProp: 'onSelectionChange', - defaultValue: null, }, density: { helperText: 'The [density](https://mui.com/x/react-data-grid/accessibility/#density-prop) of the rows. Possible values are `compact`, `standard`, or `comfortable`.', - typeDef: { type: 'string', enum: ['compact', 'standard', 'comfortable'] }, - defaultValue: 'compact', + typeDef: { type: 'string', enum: ['compact', 'standard', 'comfortable'], default: 'compact' }, }, height: { - typeDef: { type: 'number' }, - defaultValue: 350, + typeDef: { type: 'number', default: 350 }, }, loading: { helperText: diff --git a/packages/toolpad-components/src/DatePicker.tsx b/packages/toolpad-components/src/DatePicker.tsx index 349bda1e3f1..520ea6c1c19 100644 --- a/packages/toolpad-components/src/DatePicker.tsx +++ b/packages/toolpad-components/src/DatePicker.tsx @@ -112,9 +112,8 @@ export default createComponent(DatePicker, { argTypes: { value: { helperText: 'The currently selected date.', - typeDef: { type: 'string' }, + typeDef: { type: 'string', default: '' }, onChangeProp: 'onChange', - defaultValue: '', defaultValueProp: 'defaultValue', }, format: { @@ -122,13 +121,12 @@ export default createComponent(DatePicker, { 'The [format](https://day.js.org/docs/en/display/format) of the date in the UI. The value for the bindings will always be in the `YYYY-MM-DD` format. Leave empty to let the end-user locale define the format.', typeDef: { type: 'string', + default: '', }, - defaultValue: '', }, defaultValue: { helperText: 'A default value for the date picker.', - typeDef: { type: 'string' }, - defaultValue: '', + typeDef: { type: 'string', default: '' }, }, label: { helperText: 'A label that describes the content of the date picker. e.g. "Arrival date".', @@ -137,13 +135,11 @@ export default createComponent(DatePicker, { variant: { helperText: 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-button/#basic-button). Possible values are `outlined`, `filled` or `standard`', - typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, - defaultValue: 'outlined', + typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'], default: 'outlined' }, }, size: { helperText: 'The size of the component. One of `small`, or `medium`.', - typeDef: { type: 'string', enum: ['small', 'medium'] }, - defaultValue: 'small', + typeDef: { type: 'string', enum: ['small', 'medium'], default: 'small' }, }, fullWidth: { helperText: 'Whether the button should occupy all available horizontal space.', diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index 69edc5447aa..6e802990b4d 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -75,8 +75,7 @@ export default createComponent(FilePicker, { }, multiple: { helperText: 'Whether the FilePicker should accept multiple files.', - typeDef: { type: 'boolean' }, - defaultValue: true, + typeDef: { type: 'boolean', default: true }, }, disabled: { helperText: 'Whether the FilePicker is disabled.', diff --git a/packages/toolpad-components/src/Image.tsx b/packages/toolpad-components/src/Image.tsx index 0b1de48fc91..777ce63b1ae 100644 --- a/packages/toolpad-components/src/Image.tsx +++ b/packages/toolpad-components/src/Image.tsx @@ -65,28 +65,27 @@ export default createComponent(Image, { alt: { helperText: "The `alt` attribute holds a text description of the image. screen readers read this description out to their users so they know what the image means. Alt text is also displayed on the page if the image can't be loaded for some reason: for example, network errors, content blocking, or linkrot.", - typeDef: { type: 'string' }, - defaultValue: '', + typeDef: { type: 'string', default: '' }, }, fit: { helperText: 'Defines how the image should [resize](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) to its container.', - typeDef: { type: 'string', enum: ['contain', 'cover', 'fill', 'none', 'scale-down'] }, - defaultValue: 'contain', + typeDef: { + type: 'string', + enum: ['contain', 'cover', 'fill', 'none', 'scale-down'], + default: 'contain', + }, }, width: { helperText: 'The image width in pixels', - typeDef: { type: 'number' }, - defaultValue: 400, + typeDef: { type: 'number', default: 400 }, }, height: { - typeDef: { type: 'number' }, - defaultValue: 300, + typeDef: { type: 'number', default: 300 }, }, loading: { helperText: 'Displays a loading animation indicating the image is still loading', - typeDef: { type: 'boolean' }, - defaultValue: false, + typeDef: { type: 'boolean', default: false }, }, sx: { helperText: SX_PROP_HELPER_TEXT, diff --git a/packages/toolpad-components/src/PageColumn.tsx b/packages/toolpad-components/src/PageColumn.tsx index 57cd98faf53..70c9d106b31 100644 --- a/packages/toolpad-components/src/PageColumn.tsx +++ b/packages/toolpad-components/src/PageColumn.tsx @@ -26,8 +26,7 @@ function PageColumn({ gap, children }: PageColumnProps) { export default createComponent(PageColumn, { argTypes: { gap: { - typeDef: { type: 'number' }, - defaultValue: 1, + typeDef: { type: 'number', default: 1 }, }, children: { typeDef: { type: 'element' }, diff --git a/packages/toolpad-components/src/PageRow.tsx b/packages/toolpad-components/src/PageRow.tsx index 35a011bb7ad..27e72c5eac7 100644 --- a/packages/toolpad-components/src/PageRow.tsx +++ b/packages/toolpad-components/src/PageRow.tsx @@ -31,8 +31,7 @@ function PageRow({ layoutColumnSizes = [], gap, children }: PageRowProps) { export default createComponent(PageRow, { argTypes: { gap: { - typeDef: { type: 'number' }, - defaultValue: 1, + typeDef: { type: 'number', default: 1 }, }, children: { typeDef: { type: 'element' }, diff --git a/packages/toolpad-components/src/Paper.tsx b/packages/toolpad-components/src/Paper.tsx index b0d036d403b..4bd56a000dd 100644 --- a/packages/toolpad-components/src/Paper.tsx +++ b/packages/toolpad-components/src/Paper.tsx @@ -17,8 +17,7 @@ export default createComponent(Paper, { elevation: { helperText: 'The [elevation](https://mui.com/material-ui/react-paper/#elevation) can be used to establish a hierarchy between other content. In practical terms, the elevation controls the size of the shadow applied to the surface. In dark mode, raising the elevation also makes the surface lighter.', - typeDef: { type: 'number', minimum: 0 }, - defaultValue: 1, + typeDef: { type: 'number', minimum: 0, default: 1 }, }, children: { typeDef: { type: 'element' }, diff --git a/packages/toolpad-components/src/Select.tsx b/packages/toolpad-components/src/Select.tsx index 748f1daee7a..5078e61bf2c 100644 --- a/packages/toolpad-components/src/Select.tsx +++ b/packages/toolpad-components/src/Select.tsx @@ -54,37 +54,35 @@ export default createComponent(Select, { argTypes: { options: { helperText: 'The available options to select from.', - typeDef: { type: 'array', schema: '/schemas/SelectOptions.json' }, + typeDef: { type: 'array', schema: '/schemas/SelectOptions.json', default: [] }, control: { type: 'SelectOptions' }, - defaultValue: [], }, value: { helperText: 'The currently selected value.', - typeDef: { type: 'string' }, + typeDef: { type: 'string', default: '' }, onChangeProp: 'onChange', - defaultValue: '', defaultValueProp: 'defaultValue', }, defaultValue: { helperText: 'A default value.', - typeDef: { type: 'string' }, - defaultValue: '', + typeDef: { type: 'string', default: '' }, }, label: { helperText: 'A label that describes the option that can be selected. e.g. "Country".', - typeDef: { type: 'string' }, - defaultValue: '', + typeDef: { type: 'string', default: '' }, }, variant: { helperText: 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-button/#basic-button). Possible values are `outlined`, `filled` or `standard`', - typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, - defaultValue: 'outlined', + typeDef: { + type: 'string', + enum: ['outlined', 'filled', 'standard'], + default: 'outlined', + }, }, size: { helperText: 'The size of the select. One of `small`, or `medium`.', - typeDef: { type: 'string', enum: ['small', 'medium'] }, - defaultValue: 'small', + typeDef: { type: 'string', enum: ['small', 'medium'], default: 'small' }, }, fullWidth: { helperText: 'Whether the select should occupy all available horizontal space.', diff --git a/packages/toolpad-components/src/Stack.tsx b/packages/toolpad-components/src/Stack.tsx index deff26818f6..f96889a4d11 100644 --- a/packages/toolpad-components/src/Stack.tsx +++ b/packages/toolpad-components/src/Stack.tsx @@ -8,26 +8,25 @@ export default createComponent(Stack, { typeDef: { type: 'string', enum: ['row', 'row-reverse', 'column', 'column-reverse'], + default: 'row', }, - defaultValue: 'row', }, alignItems: { typeDef: { type: 'string', enum: ['start', 'center', 'end', 'stretch', 'baseline'], + default: 'start', }, - defaultValue: 'start', }, justifyContent: { typeDef: { type: 'string', enum: ['start', 'center', 'end', 'space-between', 'space-around', 'space-evenly'], + default: 'start', }, - defaultValue: 'start', }, gap: { - typeDef: { type: 'number' }, - defaultValue: 2, + typeDef: { type: 'number', default: 2 }, }, margin: { typeDef: { type: 'number' }, diff --git a/packages/toolpad-components/src/Tabs.tsx b/packages/toolpad-components/src/Tabs.tsx index bacf5f66858..e51f33d56db 100644 --- a/packages/toolpad-components/src/Tabs.tsx +++ b/packages/toolpad-components/src/Tabs.tsx @@ -40,20 +40,21 @@ export default createComponent(Tabs, { }, defaultValue: { label: 'Default active tab', - typeDef: { type: 'string' }, - defaultValue: 'tab-one', + typeDef: { type: 'string', default: 'tab-one' }, helperText: 'The tab which will be active by default.', }, tabs: { - typeDef: { type: 'array' }, - defaultValue: [ - { - title: 'Tab one', - name: 'tab-one', - }, - { title: 'Tab two', name: 'tab-two' }, - { title: 'Tab three', name: 'tab-three' }, - ], + typeDef: { + type: 'array', + default: [ + { + title: 'Tab one', + name: 'tab-one', + }, + { title: 'Tab two', name: 'tab-two' }, + { title: 'Tab three', name: 'tab-three' }, + ], + }, helperText: 'Tabs configuration object.', }, }, diff --git a/packages/toolpad-components/src/Text.tsx b/packages/toolpad-components/src/Text.tsx index 4d0897b974c..25c1fa943d8 100644 --- a/packages/toolpad-components/src/Text.tsx +++ b/packages/toolpad-components/src/Text.tsx @@ -86,21 +86,18 @@ export default createComponent(Text, { mode: { helperText: 'Defines how the content is rendered. Either as plain text, markdown, or as a link.', - typeDef: { type: 'string', enum: ['text', 'markdown', 'link'] }, + typeDef: { type: 'string', enum: ['text', 'markdown', 'link'], default: 'text' }, label: 'Mode', - defaultValue: 'text', }, value: { helperText: 'The text content.', - typeDef: { type: 'string' }, + typeDef: { type: 'string', default: '' }, label: 'Value', - defaultValue: '', control: { type: 'markdown' }, }, href: { helperText: 'The url that is being linked.', - typeDef: { type: 'string' }, - defaultValue: 'about:blank', + typeDef: { type: 'string', default: 'about:blank' }, visible: ({ mode }) => mode === 'link', }, variant: { @@ -109,6 +106,7 @@ export default createComponent(Text, { typeDef: { type: 'string', enum: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'subtitle1', 'subtitle2', 'body1', 'body2'], + default: 'body1', }, label: 'Variant', visible: ({ mode }) => mode === 'text', @@ -116,8 +114,7 @@ export default createComponent(Text, { loading: { helperText: 'Displays a loading animation instead of the text. Can be used when the content is not available yet.', - typeDef: { type: 'boolean' }, - defaultValue: false, + typeDef: { type: 'boolean', default: false }, }, sx: { helperText: SX_PROP_HELPER_TEXT, diff --git a/packages/toolpad-components/src/TextField.tsx b/packages/toolpad-components/src/TextField.tsx index d7db9595618..961e11e8b4f 100644 --- a/packages/toolpad-components/src/TextField.tsx +++ b/packages/toolpad-components/src/TextField.tsx @@ -31,15 +31,13 @@ export default createComponent(TextField, { argTypes: { value: { helperText: 'The value that is controlled by this text input.', - typeDef: { type: 'string' }, + typeDef: { type: 'string', default: '' }, onChangeProp: 'onChange', - defaultValue: '', defaultValueProp: 'defaultValue', }, defaultValue: { helperText: 'A default value for when the inoput is still empty.', - typeDef: { type: 'string' }, - defaultValue: '', + typeDef: { type: 'string', default: '' }, }, label: { helperText: 'A label that describes the content of the text field. e.g. "First name".', @@ -48,13 +46,11 @@ export default createComponent(TextField, { variant: { helperText: 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-button/#basic-button). Possible values are `outlined`, `filled` or `standard`', - typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, - defaultValue: 'outlined', + typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'], default: 'outlined' }, }, size: { helperText: 'The size of the input. One of `small`, or `medium`.', - typeDef: { type: 'string', enum: ['small', 'medium'] }, - defaultValue: 'small', + typeDef: { type: 'string', enum: ['small', 'medium'], default: 'small' }, }, fullWidth: { helperText: 'Whether the input should occupy all available horizontal space.', diff --git a/packages/toolpad-core/src/types.ts b/packages/toolpad-core/src/types.ts index d77945e9971..4b5260c0b4b 100644 --- a/packages/toolpad-core/src/types.ts +++ b/packages/toolpad-core/src/types.ts @@ -76,31 +76,37 @@ export type SlotType = 'single' | 'multiple' | 'layout'; export interface ValueTypeBase { type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event'; + default?: unknown; } export interface StringValueType extends ValueTypeBase { type: 'string'; enum?: string[]; + default?: string; } export interface NumberValueType extends ValueTypeBase { type: 'number'; minimum?: number; maximum?: number; + default?: number; } export interface BooleanValueType extends ValueTypeBase { type: 'boolean'; + default?: boolean; } export interface ObjectValueType extends ValueTypeBase { type: 'object'; schema?: string; + default?: any; } export interface ArrayValueType extends ValueTypeBase { type: 'array'; schema?: string; + default?: any[]; } export interface ElementValueType extends ValueTypeBase { @@ -178,6 +184,7 @@ export interface ArgTypeDefinition

{ description?: string; /** * A default value for the property. + * @deprecated Use `typeDef.default` instead. */ defaultValue?: V; /**