From 697c19d97ee5baaec5553497da7f307b0c0d3be9 Mon Sep 17 00:00:00 2001 From: Vinicius Goulart Date: Tue, 20 Aug 2024 01:48:09 +0200 Subject: [PATCH] feat: add filepicker properties panel --- .../src/features/properties-panel/Util.js | 1 + .../properties-panel/entries/AcceptEntry.js | 65 +++++++++++++++++++ .../entries/DefaultValueEntry.js | 12 ++-- .../properties-panel/entries/MultipleEntry.js | 43 ++++++++++++ .../properties-panel/entries/index.js | 2 + .../properties-panel/groups/GeneralGroup.js | 4 ++ .../properties-panel/PropertiesPanel.spec.js | 28 ++++++++ .../groups/GeneralGroup.spec.js | 25 +++++-- packages/form-js-editor/test/spec/form.json | 8 +++ 9 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 packages/form-js-editor/src/features/properties-panel/entries/AcceptEntry.js create mode 100644 packages/form-js-editor/src/features/properties-panel/entries/MultipleEntry.js diff --git a/packages/form-js-editor/src/features/properties-panel/Util.js b/packages/form-js-editor/src/features/properties-panel/Util.js index ee23d4fa4..bfebec1ea 100644 --- a/packages/form-js-editor/src/features/properties-panel/Util.js +++ b/packages/form-js-editor/src/features/properties-panel/Util.js @@ -86,6 +86,7 @@ export const INPUTS = [ 'taglist', 'textfield', 'textarea', + 'filepicker', ]; export const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist']; diff --git a/packages/form-js-editor/src/features/properties-panel/entries/AcceptEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/AcceptEntry.js new file mode 100644 index 000000000..3ef481ee1 --- /dev/null +++ b/packages/form-js-editor/src/features/properties-panel/entries/AcceptEntry.js @@ -0,0 +1,65 @@ +import { get } from 'min-dash'; + +import { useService, useVariables } from '../hooks'; + +import { FeelTemplatingEntry, isFeelEntryEdited } from '@bpmn-io/properties-panel'; + +export function AcceptEntry(props) { + const { editField, field } = props; + + const entries = []; + + entries.push({ + id: 'accept', + component: Accept, + editField: editField, + field: field, + isEdited: isFeelEntryEdited, + isDefaultVisible: (field) => field.type === 'filepicker', + }); + + return entries; +} + +function Accept(props) { + const { editField, field, id } = props; + + const debounce = useService('debounce'); + + const variables = useVariables().map((name) => ({ name })); + + const path = ['accept']; + + const getValue = () => { + return get(field, path, ''); + }; + + const setValue = (value) => { + return editField(field, path, value); + }; + + return FeelTemplatingEntry({ + debounce, + element: field, + getValue, + id, + label: 'Supported file formats', + singleLine: true, + setValue, + variables, + description, + }); +} + +// helpers ////////// + +const description = ( + <> + A comma-separated list of{' '} + + file type specifiers + + +); diff --git a/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js index ff93c718b..e46f1d0aa 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/DefaultValueEntry.js @@ -36,7 +36,7 @@ export function DefaultValueEntry(props) { }; } - const defaulValueBase = { + const defaultValueBase = { editField, field, id: 'defaultValue', @@ -44,21 +44,21 @@ export function DefaultValueEntry(props) { }; entries.push({ - ...defaulValueBase, + ...defaultValueBase, component: DefaultValueCheckbox, isEdited: isSelectEntryEdited, isDefaultVisible: isDefaultVisible((field) => field.type === 'checkbox'), }); entries.push({ - ...defaulValueBase, + ...defaultValueBase, component: DefaultValueNumber, isEdited: isTextFieldEntryEdited, isDefaultVisible: isDefaultVisible((field) => field.type === 'number'), }); entries.push({ - ...defaulValueBase, + ...defaultValueBase, component: DefaultValueSingleSelect, isEdited: isSelectEntryEdited, isDefaultVisible: isDefaultVisible((field) => field.type === 'radio' || field.type === 'select'), @@ -67,14 +67,14 @@ export function DefaultValueEntry(props) { // todo(Skaiir): implement a multiselect equivalent (cf. https://github.com/bpmn-io/form-js/issues/265) entries.push({ - ...defaulValueBase, + ...defaultValueBase, component: DefaultValueTextfield, isEdited: isTextFieldEntryEdited, isDefaultVisible: isDefaultVisible((field) => field.type === 'textfield'), }); entries.push({ - ...defaulValueBase, + ...defaultValueBase, component: DefaultValueTextarea, isEdited: isTextAreaEntryEdited, isDefaultVisible: isDefaultVisible((field) => field.type === 'textarea'), diff --git a/packages/form-js-editor/src/features/properties-panel/entries/MultipleEntry.js b/packages/form-js-editor/src/features/properties-panel/entries/MultipleEntry.js new file mode 100644 index 000000000..bc5c5cd3c --- /dev/null +++ b/packages/form-js-editor/src/features/properties-panel/entries/MultipleEntry.js @@ -0,0 +1,43 @@ +import { get } from 'min-dash'; + +import { ToggleSwitchEntry, isToggleSwitchEntryEdited } from '@bpmn-io/properties-panel'; + +export function MultipleEntry(props) { + const { editField, field } = props; + + const entries = []; + + entries.push({ + id: 'multiple', + component: Multiple, + editField: editField, + field: field, + isEdited: isToggleSwitchEntryEdited, + isDefaultVisible: (field) => field.type === 'filepicker', + }); + + return entries; +} + +function Multiple(props) { + const { editField, field, id } = props; + + const path = ['multiple']; + + const getValue = () => { + return get(field, path, ''); + }; + + const setValue = (value) => { + return editField(field, path, value); + }; + + return ToggleSwitchEntry({ + element: field, + getValue, + id, + label: 'Can upload multiple files', + inline: true, + setValue, + }); +} diff --git a/packages/form-js-editor/src/features/properties-panel/entries/index.js b/packages/form-js-editor/src/features/properties-panel/entries/index.js index bb6c79066..8484081e5 100644 --- a/packages/form-js-editor/src/features/properties-panel/entries/index.js +++ b/packages/form-js-editor/src/features/properties-panel/entries/index.js @@ -39,3 +39,5 @@ export { RowCountEntry } from './RowCountEntry'; export { HeadersSourceSelectEntry } from './HeadersSourceSelectEntry'; export { ColumnsExpressionEntry } from './ColumnsExpressionEntry'; export { StaticColumnsSourceEntry } from './StaticColumnsSourceEntry'; +export { AcceptEntry } from './AcceptEntry'; +export { MultipleEntry } from './MultipleEntry'; diff --git a/packages/form-js-editor/src/features/properties-panel/groups/GeneralGroup.js b/packages/form-js-editor/src/features/properties-panel/groups/GeneralGroup.js index 8686c074e..0b3416637 100644 --- a/packages/form-js-editor/src/features/properties-panel/groups/GeneralGroup.js +++ b/packages/form-js-editor/src/features/properties-panel/groups/GeneralGroup.js @@ -23,6 +23,8 @@ import { TableDataSourceEntry, PaginationEntry, RowCountEntry, + AcceptEntry, + MultipleEntry, } from '../entries'; export function GeneralGroup(field, editField, getService) { @@ -46,11 +48,13 @@ export function GeneralGroup(field, editField, getService) { ...ImageSourceEntry({ field, editField }), ...AltTextEntry({ field, editField }), ...SelectEntries({ field, editField }), + ...AcceptEntry({ field, editField }), ...DisabledEntry({ field, editField }), ...ReadonlyEntry({ field, editField }), ...TableDataSourceEntry({ field, editField }), ...PaginationEntry({ field, editField }), ...RowCountEntry({ field, editField }), + ...MultipleEntry({ field, editField }), ]; if (entries.length === 0) { diff --git a/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanel.spec.js b/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanel.spec.js index 9b86a5dc5..1996db4a0 100644 --- a/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanel.spec.js +++ b/packages/form-js-editor/test/spec/features/properties-panel/PropertiesPanel.spec.js @@ -3416,6 +3416,34 @@ describe('properties panel', function () { }); }); }); + + describe('filepicker', function () { + it('entries', function () { + // given + const field = schema.components.find(({ key }) => key === 'files'); + + bootstrapPropertiesPanel({ + container, + field, + }); + + // then + expectPanelStructure(container, { + General: [ + 'Field label', + 'Field description', + 'Key', + 'Supported file formats', + 'Disabled', + 'Read only', + 'Can upload multiple files', + ], + Condition: [], + Validation: ['Required'], + 'Custom properties': [], + }); + }); + }); }); describe('custom properties', function () { diff --git a/packages/form-js-editor/test/spec/features/properties-panel/groups/GeneralGroup.spec.js b/packages/form-js-editor/test/spec/features/properties-panel/groups/GeneralGroup.spec.js index 0811a9851..092480d4f 100644 --- a/packages/form-js-editor/test/spec/features/properties-panel/groups/GeneralGroup.spec.js +++ b/packages/form-js-editor/test/spec/features/properties-panel/groups/GeneralGroup.spec.js @@ -511,11 +511,14 @@ describe('GeneralGroup', function () { }); describe('for all other INPUTS', () => { - const otherInputTypes = INPUTS.filter((i) => !OPTIONS_INPUTS.includes(i)); + const nonDefaultValueInputs = new Set(['datetime', 'filepicker']); + const defaultValueInputs = INPUTS.filter( + (input) => !OPTIONS_INPUTS.includes(input) && !nonDefaultValueInputs.has(input), + ); it('should render', function () { // given - for (const type of otherInputTypes) { + for (const type of defaultValueInputs) { const field = { type }; // when @@ -524,8 +527,22 @@ describe('GeneralGroup', function () { // then const defaultValueEntry = findEntry('defaultValue', container); - if (type === 'datetime') expect(defaultValueEntry).to.not.exist; - else expect(defaultValueEntry).to.exist; + expect(defaultValueEntry).to.exist; + } + }); + + it('should not render', function () { + // given + for (const type of nonDefaultValueInputs) { + const field = { type }; + + // when + const { container } = renderGeneralGroup({ field }); + + // then + const defaultValueEntry = findEntry('defaultValue', container); + + expect(defaultValueEntry).to.not.exist; } }); }); diff --git a/packages/form-js-editor/test/spec/form.json b/packages/form-js-editor/test/spec/form.json index 375b4fafc..f8ac1c932 100644 --- a/packages/form-js-editor/test/spec/form.json +++ b/packages/form-js-editor/test/spec/form.json @@ -238,6 +238,14 @@ "alt": "The bpmn.io logo", "type": "image" }, + { + "label": "Image files", + "type": "filepicker", + "id": "files", + "key": "files", + "multiple": true, + "accept": ".jpg,.png" + }, { "id": "Spacer_1", "type": "spacer",