From c9451b8ff9a2c403bfcd2937a710148e1752172a Mon Sep 17 00:00:00 2001 From: bytasv Date: Mon, 9 Jan 2023 16:07:15 +0200 Subject: [PATCH 01/10] Add FilePicker component --- .../ComponentCatalog/ComponentCatalogItem.tsx | 2 + .../src/toolpad/propertyControls/file.tsx | 31 ++++++ .../src/toolpad/propertyControls/index.tsx | 4 + .../src/toolpadComponents/index.tsx | 1 + .../toolpad-components/src/FilePicker.tsx | 94 +++++++++++++++++++ packages/toolpad-components/src/index.tsx | 2 + packages/toolpad-core/src/types.ts | 12 ++- 7 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 packages/toolpad-app/src/toolpad/propertyControls/file.tsx create mode 100644 packages/toolpad-components/src/FilePicker.tsx diff --git a/packages/toolpad-app/src/toolpad/AppEditor/PageEditor/ComponentCatalog/ComponentCatalogItem.tsx b/packages/toolpad-app/src/toolpad/AppEditor/PageEditor/ComponentCatalog/ComponentCatalogItem.tsx index 344e6447203..44af04c81fa 100644 --- a/packages/toolpad-app/src/toolpad/AppEditor/PageEditor/ComponentCatalog/ComponentCatalogItem.tsx +++ b/packages/toolpad-app/src/toolpad/AppEditor/PageEditor/ComponentCatalog/ComponentCatalogItem.tsx @@ -17,6 +17,7 @@ import DateRangeIcon from '@mui/icons-material/DateRange'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import DashboardCustomizeSharpIcon from '@mui/icons-material/DashboardCustomizeSharp'; import AddIcon from '@mui/icons-material/Add'; +import UploadFileIcon from '@mui/icons-material/UploadFile'; import NotesIcon from '@mui/icons-material/Notes'; import { SvgIconProps } from '@mui/material/SvgIcon'; @@ -35,6 +36,7 @@ const iconMap = new Map>([ ['Switch', ToggleOnIcon], ['Radio', RadioButtonCheckedIcon], ['DatePicker', DateRangeIcon], + ['FilePicker', UploadFileIcon], ['Checkbox', CheckBoxIcon], ['CodeComponent', DashboardCustomizeSharpIcon], ['CreateNew', AddIcon], diff --git a/packages/toolpad-app/src/toolpad/propertyControls/file.tsx b/packages/toolpad-app/src/toolpad/propertyControls/file.tsx new file mode 100644 index 00000000000..a6e91780c83 --- /dev/null +++ b/packages/toolpad-app/src/toolpad/propertyControls/file.tsx @@ -0,0 +1,31 @@ +import { Typography } from '@mui/material'; +import * as React from 'react'; +import type { EditorProps } from '../../types'; + +function FilePropEditor({ value = [] }: EditorProps) { + const files = Array.from(value); + const hasSelectedFiles = files?.length > 0; + + if (!hasSelectedFiles) { + return ( + + No files chosen + + ); + } + + return ( +
+ + Files: + + {files.map(({ name }) => ( + + {name} + + ))} +
+ ); +} + +export default FilePropEditor; diff --git a/packages/toolpad-app/src/toolpad/propertyControls/index.tsx b/packages/toolpad-app/src/toolpad/propertyControls/index.tsx index c22ce5ee44c..f63e485357f 100644 --- a/packages/toolpad-app/src/toolpad/propertyControls/index.tsx +++ b/packages/toolpad-app/src/toolpad/propertyControls/index.tsx @@ -11,6 +11,7 @@ import SelectOptions from './SelectOptions'; import HorizontalAlign from './HorizontalAlign'; import VerticalAlign from './VerticalAlign'; import RowIdFieldSelect from './RowIdFieldSelect'; +import file from './file'; import { EditorProps } from '../../types'; const propTypeControls: { @@ -28,6 +29,7 @@ const propTypeControls: { RowIdFieldSelect, HorizontalAlign, VerticalAlign, + file, }; function getDefaultControlForType(propType: PropValueType): React.FC> | null { @@ -44,6 +46,8 @@ function getDefaultControlForType(propType: PropValueType): React.FC([ ['DataGrid', { displayName: 'Data grid', builtIn: 'DataGrid' }], ['TextField', { displayName: 'Text field', builtIn: 'TextField' }], ['DatePicker', { displayName: 'Date picker', builtIn: 'DatePicker' }], + ['FilePicker', { displayName: 'File picker', builtIn: 'FilePicker' }], ['Text', { displayName: 'Text', builtIn: 'Text' }], ['Select', { displayName: 'Select', builtIn: 'Select' }], ['Paper', { displayName: 'Paper', builtIn: 'Paper' }], diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx new file mode 100644 index 00000000000..748373f9f9a --- /dev/null +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -0,0 +1,94 @@ +import * as React from 'react'; +import { TextField as MuiTextField, TextFieldProps as MuiTextFieldProps } from '@mui/material'; +import { createComponent } from '@mui/toolpad-core'; + +interface FullFile { + name: string; + type: string; + size: number; + base64: null | string; +} + +export type Props = MuiTextFieldProps & { + multiple: boolean; + onChange: (files: FullFile[]) => void; +}; + +const readFile = async (file: Blob): Promise => { + return new Promise((resolve, reject) => { + const readerBase64 = new FileReader(); + + readerBase64.onload = (event) => { + if (!event.target) { + reject(); + return; + } + + resolve(event.target.result as string); + }; + + readerBase64.readAsDataURL(file); + }); +}; + +function FilePicker({ multiple, onChange, ...props }: Props) { + const handleChange = async (changeEvent: React.ChangeEvent) => { + const filesPromises = Array.from(changeEvent.target.files || []).map(async (file) => { + const fullFile: FullFile = { + name: file.name, + type: file.type, + size: file.size, + base64: await readFile(file), + }; + + return fullFile; + }); + + const files = await Promise.all(filesPromises); + + onChange(files); + }; + + return ( + + ); +} + +export default createComponent(FilePicker, { + argTypes: { + value: { + typeDef: { type: 'file' }, + onChangeProp: 'onChange', + }, + label: { + typeDef: { type: 'string' }, + }, + variant: { + typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, + defaultValue: 'outlined', + }, + size: { + typeDef: { type: 'string', enum: ['small', 'normal'] }, + defaultValue: 'small', + }, + multiple: { + typeDef: { type: 'boolean' }, + defaultValue: true, + }, + fullWidth: { + typeDef: { type: 'boolean' }, + }, + disabled: { + typeDef: { type: 'boolean' }, + }, + sx: { + typeDef: { type: 'object' }, + }, + }, +}); diff --git a/packages/toolpad-components/src/index.tsx b/packages/toolpad-components/src/index.tsx index b9577f742f4..99e4e8fd4c4 100644 --- a/packages/toolpad-components/src/index.tsx +++ b/packages/toolpad-components/src/index.tsx @@ -20,5 +20,7 @@ export { default as Image } from './Image.js'; export { default as DatePicker } from './DatePicker.js'; +export { default as FilePicker } from './FilePicker.js'; + export { CUSTOM_COLUMN_TYPES, NUMBER_FORMAT_PRESETS, inferColumns, parseColumns } from './DataGrid'; export type { SerializableGridColumn, SerializableGridColumns, NumberFormat } from './DataGrid'; diff --git a/packages/toolpad-core/src/types.ts b/packages/toolpad-core/src/types.ts index db9d5a1a401..d14bdc80726 100644 --- a/packages/toolpad-core/src/types.ts +++ b/packages/toolpad-core/src/types.ts @@ -75,7 +75,7 @@ export type BindableAttrEntries = [string, BindableAttrValue][]; export type SlotType = 'single' | 'multiple' | 'layout'; export interface ValueTypeBase { - type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event'; + type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event' | 'file'; } export interface StringValueType extends ValueTypeBase { @@ -103,6 +103,10 @@ export interface ArrayValueType extends ValueTypeBase { schema?: string; } +export interface FileValueType extends ValueTypeBase { + type: 'file'; +} + export interface ElementValueType extends ValueTypeBase { type: 'element'; } @@ -138,7 +142,8 @@ export interface ArgControlSpec { | 'HorizontalAlign' | 'VerticalAlign' | 'event' - | 'RowIdFieldSelect'; // Row id field specialized select + | 'RowIdFieldSelect' // Row id field specialized select + | 'file'; } type PrimitiveValueType = @@ -146,7 +151,8 @@ type PrimitiveValueType = | NumberValueType | BooleanValueType | ObjectValueType - | ArrayValueType; + | ArrayValueType + | FileValueType; export type PropValueType = PrimitiveValueType | ElementValueType | EventValueType; From e0d87f2e2449e04af8c975622e550b3d28d7d2cb Mon Sep 17 00:00:00 2001 From: Vytautas Butkus Date: Tue, 10 Jan 2023 14:44:04 +0200 Subject: [PATCH 02/10] Update packages/toolpad-components/src/FilePicker.tsx Co-authored-by: Jan Potoms <2109932+Janpot@users.noreply.github.com> Signed-off-by: Vytautas Butkus --- packages/toolpad-components/src/FilePicker.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index 748373f9f9a..435d016f68b 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -63,7 +63,8 @@ function FilePicker({ multiple, onChange, ...props }: Props) { export default createComponent(FilePicker, { argTypes: { value: { - typeDef: { type: 'file' }, + typeDef: { type: 'object' }, + visible: false onChangeProp: 'onChange', }, label: { From 11b68e3218e7f24fb760963a23f3681fb0738533 Mon Sep 17 00:00:00 2001 From: bytasv Date: Tue, 10 Jan 2023 14:46:14 +0200 Subject: [PATCH 03/10] Feedback --- .../src/toolpad/propertyControls/file.tsx | 31 ------------------- .../src/toolpad/propertyControls/index.tsx | 4 --- .../toolpad-components/src/FilePicker.tsx | 2 +- packages/toolpad-core/src/types.ts | 12 ++----- 4 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 packages/toolpad-app/src/toolpad/propertyControls/file.tsx diff --git a/packages/toolpad-app/src/toolpad/propertyControls/file.tsx b/packages/toolpad-app/src/toolpad/propertyControls/file.tsx deleted file mode 100644 index a6e91780c83..00000000000 --- a/packages/toolpad-app/src/toolpad/propertyControls/file.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Typography } from '@mui/material'; -import * as React from 'react'; -import type { EditorProps } from '../../types'; - -function FilePropEditor({ value = [] }: EditorProps) { - const files = Array.from(value); - const hasSelectedFiles = files?.length > 0; - - if (!hasSelectedFiles) { - return ( - - No files chosen - - ); - } - - return ( -
- - Files: - - {files.map(({ name }) => ( - - {name} - - ))} -
- ); -} - -export default FilePropEditor; diff --git a/packages/toolpad-app/src/toolpad/propertyControls/index.tsx b/packages/toolpad-app/src/toolpad/propertyControls/index.tsx index f63e485357f..c22ce5ee44c 100644 --- a/packages/toolpad-app/src/toolpad/propertyControls/index.tsx +++ b/packages/toolpad-app/src/toolpad/propertyControls/index.tsx @@ -11,7 +11,6 @@ import SelectOptions from './SelectOptions'; import HorizontalAlign from './HorizontalAlign'; import VerticalAlign from './VerticalAlign'; import RowIdFieldSelect from './RowIdFieldSelect'; -import file from './file'; import { EditorProps } from '../../types'; const propTypeControls: { @@ -29,7 +28,6 @@ const propTypeControls: { RowIdFieldSelect, HorizontalAlign, VerticalAlign, - file, }; function getDefaultControlForType(propType: PropValueType): React.FC> | null { @@ -46,8 +44,6 @@ function getDefaultControlForType(propType: PropValueType): React.FC][]; export type SlotType = 'single' | 'multiple' | 'layout'; export interface ValueTypeBase { - type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event' | 'file'; + type: 'string' | 'boolean' | 'number' | 'object' | 'array' | 'element' | 'event'; } export interface StringValueType extends ValueTypeBase { @@ -103,10 +103,6 @@ export interface ArrayValueType extends ValueTypeBase { schema?: string; } -export interface FileValueType extends ValueTypeBase { - type: 'file'; -} - export interface ElementValueType extends ValueTypeBase { type: 'element'; } @@ -142,8 +138,7 @@ export interface ArgControlSpec { | 'HorizontalAlign' | 'VerticalAlign' | 'event' - | 'RowIdFieldSelect' // Row id field specialized select - | 'file'; + | 'RowIdFieldSelect'; // Row id field specialized select } type PrimitiveValueType = @@ -151,8 +146,7 @@ type PrimitiveValueType = | NumberValueType | BooleanValueType | ObjectValueType - | ArrayValueType - | FileValueType; + | ArrayValueType; export type PropValueType = PrimitiveValueType | ElementValueType | EventValueType; From a3beef0daee45ddad1044b290a27f7a149c0799a Mon Sep 17 00:00:00 2001 From: bytasv Date: Wed, 11 Jan 2023 08:20:40 +0200 Subject: [PATCH 04/10] Add helper text for FilePicker --- packages/toolpad-components/src/FilePicker.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index 45f1bd365ae..46b4209f980 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -56,11 +56,13 @@ function FilePicker({ multiple, onChange, ...props }: Props) { value={undefined} inputProps={{ multiple }} onChange={handleChange} + InputLabelProps={{ shrink: true }} /> ); } export default createComponent(FilePicker, { + helperText: 'File picker component.\nIt allows users to take select and read files.', argTypes: { value: { typeDef: { type: 'object' }, @@ -68,24 +70,31 @@ export default createComponent(FilePicker, { onChangeProp: 'onChange', }, label: { + helperText: 'A label that describes the content of the FilePicker. e.g. "Profile Image".', typeDef: { type: 'string' }, }, variant: { + helperText: + 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-text-field/#basic-textfield). Possible values are `outlined`, `filled` or `standard`.', typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, defaultValue: 'outlined', }, size: { + helperText: 'The size of the component. One of `small` or `normal`.', typeDef: { type: 'string', enum: ['small', 'normal'] }, defaultValue: 'small', }, multiple: { + helperText: 'Whether the FilePicker should accept multiple files.', typeDef: { type: 'boolean' }, defaultValue: true, }, fullWidth: { + helperText: 'Whether the FilePicker should occupy all available horizontal space.', typeDef: { type: 'boolean' }, }, disabled: { + helperText: 'Whether the FilePicker is disabled.', typeDef: { type: 'boolean' }, }, sx: { From b2daeef9c2bc91a5819f5194442b97b7059641b8 Mon Sep 17 00:00:00 2001 From: bytasv Date: Tue, 17 Jan 2023 12:58:23 +0200 Subject: [PATCH 05/10] Use button for filepicker --- .../toolpad-components/src/FilePicker.tsx | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index 46b4209f980..f608249cda9 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; -import { TextField as MuiTextField, TextFieldProps as MuiTextFieldProps } from '@mui/material'; +import { Button as MuiButton, ButtonProps } from '@mui/material'; import { createComponent } from '@mui/toolpad-core'; +import { SX_PROP_HELPER_TEXT } from './constants'; interface FullFile { name: string; @@ -9,10 +10,13 @@ interface FullFile { base64: null | string; } -export type Props = MuiTextFieldProps & { +interface Props + extends Pick { multiple: boolean; + content: string; + value: any; onChange: (files: FullFile[]) => void; -}; +} const readFile = async (file: Blob): Promise => { return new Promise((resolve, reject) => { @@ -31,7 +35,7 @@ const readFile = async (file: Blob): Promise => { }); }; -function FilePicker({ multiple, onChange, ...props }: Props) { +function FilePicker({ multiple, onChange, content, ...props }: Props) { const handleChange = async (changeEvent: React.ChangeEvent) => { const filesPromises = Array.from(changeEvent.target.files || []).map(async (file) => { const fullFile: FullFile = { @@ -50,14 +54,10 @@ function FilePicker({ multiple, onChange, ...props }: Props) { }; return ( - + + {content} + + ); } @@ -69,35 +69,42 @@ export default createComponent(FilePicker, { visible: false, onChangeProp: 'onChange', }, - label: { - helperText: 'A label that describes the content of the FilePicker. e.g. "Profile Image".', + multiple: { + helperText: 'Whether the FilePicker should accept multiple files.', + typeDef: { type: 'boolean' }, + defaultValue: true, + }, + content: { + helperText: 'Will appear as the text content of the button.', typeDef: { type: 'string' }, + defaultValue: 'Select files', }, variant: { helperText: - 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-text-field/#basic-textfield). Possible values are `outlined`, `filled` or `standard`.', - typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, - defaultValue: 'outlined', + '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', }, size: { - helperText: 'The size of the component. One of `small` or `normal`.', - typeDef: { type: 'string', enum: ['small', 'normal'] }, - defaultValue: 'small', + helperText: 'The size of the component. One of `small`, `medium`, or `large`.', + typeDef: { type: 'string', enum: ['small', 'medium', 'large'] }, + defaultValue: 'medium', }, - multiple: { - helperText: 'Whether the FilePicker should accept multiple files.', - typeDef: { type: 'boolean' }, - defaultValue: true, + color: { + helperText: 'The theme color of the component.', + typeDef: { type: 'string', enum: ['primary', 'secondary'] }, + defaultValue: 'primary', }, fullWidth: { - helperText: 'Whether the FilePicker should occupy all available horizontal space.', + helperText: 'Whether the button should occupy all available horizontal space.', typeDef: { type: 'boolean' }, }, disabled: { - helperText: 'Whether the FilePicker is disabled.', + helperText: 'Whether the button is disabled.', typeDef: { type: 'boolean' }, }, sx: { + helperText: SX_PROP_HELPER_TEXT, typeDef: { type: 'object' }, }, }, From 2e902d84acc1704ea22ef1dadec215b16f8c65b3 Mon Sep 17 00:00:00 2001 From: bytasv Date: Wed, 18 Jan 2023 13:50:53 +0200 Subject: [PATCH 06/10] Revert "Use button for filepicker" This reverts commit b2daeef9c2bc91a5819f5194442b97b7059641b8. --- .../toolpad-components/src/FilePicker.tsx | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index f608249cda9..46b4209f980 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { Button as MuiButton, ButtonProps } from '@mui/material'; +import { TextField as MuiTextField, TextFieldProps as MuiTextFieldProps } from '@mui/material'; import { createComponent } from '@mui/toolpad-core'; -import { SX_PROP_HELPER_TEXT } from './constants'; interface FullFile { name: string; @@ -10,13 +9,10 @@ interface FullFile { base64: null | string; } -interface Props - extends Pick { +export type Props = MuiTextFieldProps & { multiple: boolean; - content: string; - value: any; onChange: (files: FullFile[]) => void; -} +}; const readFile = async (file: Blob): Promise => { return new Promise((resolve, reject) => { @@ -35,7 +31,7 @@ const readFile = async (file: Blob): Promise => { }); }; -function FilePicker({ multiple, onChange, content, ...props }: Props) { +function FilePicker({ multiple, onChange, ...props }: Props) { const handleChange = async (changeEvent: React.ChangeEvent) => { const filesPromises = Array.from(changeEvent.target.files || []).map(async (file) => { const fullFile: FullFile = { @@ -54,10 +50,14 @@ function FilePicker({ multiple, onChange, content, ...props }: Props) { }; return ( - - {content} - - + ); } @@ -69,42 +69,35 @@ export default createComponent(FilePicker, { visible: false, onChangeProp: 'onChange', }, - multiple: { - helperText: 'Whether the FilePicker should accept multiple files.', - typeDef: { type: 'boolean' }, - defaultValue: true, - }, - content: { - helperText: 'Will appear as the text content of the button.', + label: { + helperText: 'A label that describes the content of the FilePicker. e.g. "Profile Image".', typeDef: { type: 'string' }, - defaultValue: 'Select files', }, 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', + 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-text-field/#basic-textfield). Possible values are `outlined`, `filled` or `standard`.', + typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, + defaultValue: 'outlined', }, size: { - helperText: 'The size of the component. One of `small`, `medium`, or `large`.', - typeDef: { type: 'string', enum: ['small', 'medium', 'large'] }, - defaultValue: 'medium', + helperText: 'The size of the component. One of `small` or `normal`.', + typeDef: { type: 'string', enum: ['small', 'normal'] }, + defaultValue: 'small', }, - color: { - helperText: 'The theme color of the component.', - typeDef: { type: 'string', enum: ['primary', 'secondary'] }, - defaultValue: 'primary', + multiple: { + helperText: 'Whether the FilePicker should accept multiple files.', + typeDef: { type: 'boolean' }, + defaultValue: true, }, fullWidth: { - helperText: 'Whether the button should occupy all available horizontal space.', + helperText: 'Whether the FilePicker should occupy all available horizontal space.', typeDef: { type: 'boolean' }, }, disabled: { - helperText: 'Whether the button is disabled.', + helperText: 'Whether the FilePicker is disabled.', typeDef: { type: 'boolean' }, }, sx: { - helperText: SX_PROP_HELPER_TEXT, typeDef: { type: 'object' }, }, }, From 5f0daa4795a0248c43dc8ac56bb5674a7a7017ae Mon Sep 17 00:00:00 2001 From: bytasv Date: Wed, 18 Jan 2023 14:17:48 +0200 Subject: [PATCH 07/10] Add file picker tests --- test/integration/file-picker/dom.json | 132 +++++++++++++++++++++ test/integration/file-picker/index.spec.ts | 28 +++++ test/integration/file-picker/test.txt | 1 + 3 files changed, 161 insertions(+) create mode 100644 test/integration/file-picker/dom.json create mode 100644 test/integration/file-picker/index.spec.ts create mode 100644 test/integration/file-picker/test.txt diff --git a/test/integration/file-picker/dom.json b/test/integration/file-picker/dom.json new file mode 100644 index 00000000000..45048d87e4e --- /dev/null +++ b/test/integration/file-picker/dom.json @@ -0,0 +1,132 @@ +{ + "root": "6j034xh", + "nodes": { + "0d23u7b": { + "id": "0d23u7b", + "name": "pageRow2", + "type": "element", + "props": {}, + "layout": {}, + "parentId": "6k134xi", + "attributes": { + "component": { + "type": "const", + "value": "PageRow" + } + }, + "parentProp": "children", + "parentIndex": "a2" + }, + "5z83u5r": { + "id": "5z83u5r", + "name": "text1", + "type": "element", + "props": { + "value": { + "type": "jsExpression", + "value": "filePicker.value && filePicker.value[0].base64 ? \"Uploaded: base64\" : \"Empty\"\n" + } + }, + "layout": {}, + "parentId": "0d23u7b", + "attributes": { + "component": { + "type": "const", + "value": "Text" + } + }, + "parentProp": "children", + "parentIndex": "a1" + }, + "6j034xh": { + "id": "6j034xh", + "name": "Application", + "type": "app", + "parentId": null, + "attributes": {}, + "parentProp": null, + "parentIndex": null + }, + "6k134xi": { + "id": "6k134xi", + "name": "page1", + "type": "page", + "parentId": "6j034xh", + "attributes": { + "title": { + "type": "const", + "value": "Page 1" + }, + "module": { + "type": "const", + "value": "// All properties of this object will be available on the global scope in bindings\nexport const globalScope = {\n value: 3,\n};\n" + } + }, + "parentProp": "pages", + "parentIndex": "a0" + }, + "9e23ub5": { + "id": "9e23ub5", + "name": "pageRow", + "type": "element", + "props": {}, + "layout": {}, + "parentId": "6k134xi", + "attributes": { + "component": { + "type": "const", + "value": "PageRow" + } + }, + "parentProp": "children", + "parentIndex": "a1V" + }, + "f403uuw": { + "id": "f403uuw", + "name": "filePicker", + "type": "element", + "props": { + "label": { + "type": "const", + "value": "File pick" + }, + "variant": { + "type": "const", + "value": "filled" + } + }, + "layout": {}, + "parentId": "9e23ub5", + "attributes": { + "component": { + "type": "const", + "value": "FilePicker" + } + }, + "parentProp": "children", + "parentIndex": "a0" + }, + "qc03u5d": { + "id": "qc03u5d", + "name": "text", + "type": "element", + "props": { + "value": { + "type": "jsExpression", + "value": "\"name: \" + (filePicker.value && filePicker.value[0].name)\n" + } + }, + "layout": {}, + "parentId": "0d23u7b", + "attributes": { + "component": { + "type": "const", + "value": "Text" + } + }, + "parentProp": "children", + "parentIndex": "a0" + } + }, + "version": 5 +} diff --git a/test/integration/file-picker/index.spec.ts b/test/integration/file-picker/index.spec.ts new file mode 100644 index 00000000000..014cf5ed15b --- /dev/null +++ b/test/integration/file-picker/index.spec.ts @@ -0,0 +1,28 @@ +import * as path from 'path'; +import { ToolpadEditor } from '../../models/ToolpadEditor'; +import { test, expect } from '../../playwright/test'; +import { readJsonFile } from '../../utils/fs'; +import generateId from '../../utils/generateId'; + +test('File picker component', async ({ page, browserName, api }) => { + const dom = await readJsonFile(path.resolve(__dirname, './dom.json')); + const testFilePath = path.resolve(__dirname, './test.txt'); + + const app = await api.mutation.createApp(`App ${generateId()}`, { + from: { kind: 'dom', dom }, + }); + + const editorModel = new ToolpadEditor(page, browserName); + editorModel.goto(app.id); + + await editorModel.waitForOverlay(); + + const filePicker = editorModel.pageRoot.getByText('File pick'); + + await expect(filePicker).toBeVisible(); + + await filePicker.setInputFiles(testFilePath); + + await expect(editorModel.pageRoot.getByText('name: test.txt')).toBeVisible(); + await expect(editorModel.pageRoot.getByText('Uploaded: base64')).toBeVisible(); +}); diff --git a/test/integration/file-picker/test.txt b/test/integration/file-picker/test.txt new file mode 100644 index 00000000000..153d19401bc --- /dev/null +++ b/test/integration/file-picker/test.txt @@ -0,0 +1 @@ +works From 890da92e5e9fb81d102f1d5f2d0ca75f5780e577 Mon Sep 17 00:00:00 2001 From: bytasv Date: Fri, 20 Jan 2023 09:26:06 +0200 Subject: [PATCH 08/10] Remove possibly incompatible props --- packages/toolpad-components/src/FilePicker.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/toolpad-components/src/FilePicker.tsx b/packages/toolpad-components/src/FilePicker.tsx index 46b4209f980..69edc5447aa 100644 --- a/packages/toolpad-components/src/FilePicker.tsx +++ b/packages/toolpad-components/src/FilePicker.tsx @@ -73,26 +73,11 @@ export default createComponent(FilePicker, { helperText: 'A label that describes the content of the FilePicker. e.g. "Profile Image".', typeDef: { type: 'string' }, }, - variant: { - helperText: - 'One of the available MUI TextField [variants](https://mui.com/material-ui/react-text-field/#basic-textfield). Possible values are `outlined`, `filled` or `standard`.', - typeDef: { type: 'string', enum: ['outlined', 'filled', 'standard'] }, - defaultValue: 'outlined', - }, - size: { - helperText: 'The size of the component. One of `small` or `normal`.', - typeDef: { type: 'string', enum: ['small', 'normal'] }, - defaultValue: 'small', - }, multiple: { helperText: 'Whether the FilePicker should accept multiple files.', typeDef: { type: 'boolean' }, defaultValue: true, }, - fullWidth: { - helperText: 'Whether the FilePicker should occupy all available horizontal space.', - typeDef: { type: 'boolean' }, - }, disabled: { helperText: 'Whether the FilePicker is disabled.', typeDef: { type: 'boolean' }, From 2f6aafed924635c54838547313ec3a3dcf6c9208 Mon Sep 17 00:00:00 2001 From: bytasv Date: Fri, 20 Jan 2023 11:15:20 +0200 Subject: [PATCH 09/10] Fix spec --- test/integration/file-picker/dom.json | 4 ++-- test/integration/file-picker/index.spec.ts | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/integration/file-picker/dom.json b/test/integration/file-picker/dom.json index 45048d87e4e..75a6670af1d 100644 --- a/test/integration/file-picker/dom.json +++ b/test/integration/file-picker/dom.json @@ -24,7 +24,7 @@ "props": { "value": { "type": "jsExpression", - "value": "filePicker.value && filePicker.value[0].base64 ? \"Uploaded: base64\" : \"Empty\"\n" + "value": "filePicker.value && filePicker.value[0].base64 ? \"Uploaded\" : \"Empty\"\n" } }, "layout": {}, @@ -113,7 +113,7 @@ "props": { "value": { "type": "jsExpression", - "value": "\"name: \" + (filePicker.value && filePicker.value[0].name)\n" + "value": "filePicker.value && filePicker.value[0].name\n" } }, "layout": {}, diff --git a/test/integration/file-picker/index.spec.ts b/test/integration/file-picker/index.spec.ts index 014cf5ed15b..a568e812634 100644 --- a/test/integration/file-picker/index.spec.ts +++ b/test/integration/file-picker/index.spec.ts @@ -4,7 +4,7 @@ import { test, expect } from '../../playwright/test'; import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; -test('File picker component', async ({ page, browserName, api }) => { +test.only('File picker component', async ({ page, browserName, api }) => { const dom = await readJsonFile(path.resolve(__dirname, './dom.json')); const testFilePath = path.resolve(__dirname, './test.txt'); @@ -12,17 +12,19 @@ test('File picker component', async ({ page, browserName, api }) => { from: { kind: 'dom', dom }, }); + await page.pause(); + const editorModel = new ToolpadEditor(page, browserName); editorModel.goto(app.id); await editorModel.waitForOverlay(); - const filePicker = editorModel.pageRoot.getByText('File pick'); + const filePicker = editorModel.pageRoot.locator('label'); await expect(filePicker).toBeVisible(); await filePicker.setInputFiles(testFilePath); - await expect(editorModel.pageRoot.getByText('name: test.txt')).toBeVisible(); - await expect(editorModel.pageRoot.getByText('Uploaded: base64')).toBeVisible(); + await expect(editorModel.pageRoot.getByText('test.txt')).toBeVisible(); + await expect(editorModel.pageRoot.getByText('Uploaded')).toBeVisible(); }); From b2b7e5ea09a1b6e96dab43dfe2127c6fffdafa62 Mon Sep 17 00:00:00 2001 From: bytasv Date: Fri, 20 Jan 2023 11:54:03 +0200 Subject: [PATCH 10/10] Remove only --- test/integration/file-picker/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/file-picker/index.spec.ts b/test/integration/file-picker/index.spec.ts index a568e812634..7d5b5023ec5 100644 --- a/test/integration/file-picker/index.spec.ts +++ b/test/integration/file-picker/index.spec.ts @@ -4,7 +4,7 @@ import { test, expect } from '../../playwright/test'; import { readJsonFile } from '../../utils/fs'; import generateId from '../../utils/generateId'; -test.only('File picker component', async ({ page, browserName, api }) => { +test('File picker component', async ({ page, browserName, api }) => { const dom = await readJsonFile(path.resolve(__dirname, './dom.json')); const testFilePath = path.resolve(__dirname, './test.txt');