├── .codecov.yml ├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ADD_TO_HTO_PROJECT.yml │ ├── CI.yml │ ├── DEPLOY_PLAYGROUND_PREVIEW.yml │ ├── MERGE_MASTER_TO_DEVELOP.yml │ ├── POST_RELEASE.yml │ ├── TASKLIST_CARBONISATION.yml │ ├── TEARDOWN_PLAYGROUND_PREVIEW.yml │ ├── UPDATE_VISUAL_SNAPSHOTS.yml │ └── VISUAL_REGRESSION.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── LICENSE ├── README.md ├── docs └── FORM_SCHEMA.md ├── e2e ├── README.md ├── carbon │ ├── carbon.scss │ ├── index.html │ ├── index.js │ └── theme.scss ├── index.html ├── main.js ├── renderSchema.js ├── styles.scss ├── test-fixtures.js ├── theming │ ├── index.html │ ├── index.js │ └── theme.scss └── visual │ ├── carbon-styles.spec.js │ ├── carbon-styles.spec.js-snapshots │ ├── carbon-styles-1-chromium-linux.png │ ├── carbon-styles-1-firefox-linux.png │ └── carbon-styles-1-webkit-linux.png │ ├── empty-ui.spec.js │ ├── empty-ui.spec.js-snapshots │ ├── empty-playground-1-chromium-linux.png │ ├── empty-playground-1-firefox-linux.png │ └── empty-playground-1-webkit-linux.png │ ├── fixtures │ ├── complex.json │ ├── empty.json │ ├── form.json │ └── groups.json │ ├── groups-ui.spec.js │ ├── groups-ui.spec.js-snapshots │ ├── groups-playground-1-chromium-linux.png │ ├── groups-playground-1-firefox-linux.png │ └── groups-playground-1-webkit-linux.png │ ├── no-theme.spec.js │ ├── no-theme.spec.js-snapshots │ ├── no-theme---editor-1-chromium-linux.png │ ├── no-theme---editor-1-firefox-linux.png │ ├── no-theme---editor-1-webkit-linux.png │ ├── no-theme---viewer-1-chromium-linux.png │ ├── no-theme---viewer-1-firefox-linux.png │ └── no-theme---viewer-1-webkit-linux.png │ ├── theming.spec.js │ └── theming.spec.js-snapshots │ ├── theming---editor-1-chromium-linux.png │ ├── theming---editor-1-firefox-linux.png │ ├── theming---editor-1-webkit-linux.png │ ├── theming---viewer-1-chromium-linux.png │ ├── theming---viewer-1-firefox-linux.png │ └── theming---viewer-1-webkit-linux.png ├── eslint.config.mjs ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── form-js-carbon-styles │ ├── LICENSE │ ├── karma.conf.js │ ├── package.json │ ├── src │ │ ├── carbon-styles.js │ │ ├── carbon-styles.scss │ │ └── types │ │ │ └── carbon-styles.d.ts │ └── test │ │ ├── TestHelper.js │ │ ├── spec │ │ ├── carbon-styles.spec.js │ │ ├── complex.json │ │ └── theme.scss │ │ ├── test.css │ │ └── testBundle.js ├── form-js-editor │ ├── LICENSE │ ├── README.md │ ├── assets │ │ ├── form-js-editor-base.css │ │ └── index.scss │ ├── karma.conf.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── FormEditor.js │ │ ├── core │ │ │ ├── Debounce.js │ │ │ ├── EventBus.js │ │ │ ├── FormFieldRegistry.js │ │ │ ├── FormLayoutValidator.js │ │ │ ├── FormLayouter.js │ │ │ └── index.js │ │ ├── features │ │ │ ├── SectionModuleBase.js │ │ │ ├── dragging │ │ │ │ ├── Dragging.js │ │ │ │ └── index.js │ │ │ ├── editor-actions │ │ │ │ ├── FormEditorActions.js │ │ │ │ └── index.js │ │ │ ├── expression-language │ │ │ │ ├── EditorTemplating.js │ │ │ │ └── index.js │ │ │ ├── keyboard │ │ │ │ ├── FormEditorKeyboardBindings.js │ │ │ │ └── index.js │ │ │ ├── modeling │ │ │ │ ├── FormLayoutUpdater.js │ │ │ │ ├── Modeling.js │ │ │ │ ├── behavior │ │ │ │ │ ├── ColumnsSourceBehavior.js │ │ │ │ │ ├── IdBehavior.js │ │ │ │ │ ├── KeyBehavior.js │ │ │ │ │ ├── OptionsSourceBehavior.js │ │ │ │ │ ├── PathBehavior.js │ │ │ │ │ ├── TableDataSourceBehavior.js │ │ │ │ │ ├── ValidateBehavior.js │ │ │ │ │ └── index.js │ │ │ │ ├── cmd │ │ │ │ │ ├── AddFormFieldHandler.js │ │ │ │ │ ├── EditFormFieldHandler.js │ │ │ │ │ ├── MoveFormFieldHandler.js │ │ │ │ │ ├── RemoveFormFieldHandler.js │ │ │ │ │ ├── UpdateIdClaimHandler.js │ │ │ │ │ ├── UpdateKeyClaimHandler.js │ │ │ │ │ ├── UpdatePathClaimHandler.js │ │ │ │ │ └── Util.js │ │ │ │ └── index.js │ │ │ ├── palette │ │ │ │ ├── PaletteRenderer.js │ │ │ │ ├── components │ │ │ │ │ ├── Palette.js │ │ │ │ │ └── PaletteEntry.js │ │ │ │ └── index.js │ │ │ ├── properties-panel │ │ │ │ ├── PropertiesPanel.js │ │ │ │ ├── PropertiesPanelHeaderProvider.js │ │ │ │ ├── PropertiesPanelModule.js │ │ │ │ ├── PropertiesPanelPlaceholderProvider.js │ │ │ │ ├── PropertiesPanelRenderer.js │ │ │ │ ├── PropertiesProvider.js │ │ │ │ ├── Util.js │ │ │ │ ├── components │ │ │ │ │ ├── AutoFocusSelect.js │ │ │ │ │ └── index.js │ │ │ │ ├── context │ │ │ │ │ ├── FormPropertiesPanelContext.js │ │ │ │ │ └── index.js │ │ │ │ ├── entries │ │ │ │ │ ├── AcceptEntry.js │ │ │ │ │ ├── ActionEntry.js │ │ │ │ │ ├── AdornerEntry.js │ │ │ │ │ ├── AltTextEntry.js │ │ │ │ │ ├── ColumnEntry.js │ │ │ │ │ ├── ColumnsEntry.js │ │ │ │ │ ├── ColumnsExpressionEntry.js │ │ │ │ │ ├── ConditionEntry.js │ │ │ │ │ ├── CustomValueEntry.js │ │ │ │ │ ├── DateTimeConstraintsEntry.js │ │ │ │ │ ├── DateTimeEntry.js │ │ │ │ │ ├── DateTimeFormatEntry.js │ │ │ │ │ ├── DefaultValueEntry.js │ │ │ │ │ ├── DescriptionEntry.js │ │ │ │ │ ├── DisabledEntry.js │ │ │ │ │ ├── DocumentsDataSource.js │ │ │ │ │ ├── ExpressionFieldEntries.js │ │ │ │ │ ├── GroupAppearanceEntry.js │ │ │ │ │ ├── HeadersSourceSelectEntry.js │ │ │ │ │ ├── HeightEntry.js │ │ │ │ │ ├── HtmlEntry.js │ │ │ │ │ ├── IFrameHeightEntry.js │ │ │ │ │ ├── IFrameUrlEntry.js │ │ │ │ │ ├── IdEntry.js │ │ │ │ │ ├── ImageSourceEntry.js │ │ │ │ │ ├── InputKeyOptionsSourceEntry.js │ │ │ │ │ ├── KeyEntry.js │ │ │ │ │ ├── LabelEntry.js │ │ │ │ │ ├── LayouterAppearanceEntry.js │ │ │ │ │ ├── MaxHeightEntry.js │ │ │ │ │ ├── MultipleEntry.js │ │ │ │ │ ├── NumberEntries.js │ │ │ │ │ ├── NumberSerializationEntry.js │ │ │ │ │ ├── OptionsExpressionEntry.js │ │ │ │ │ ├── OptionsSourceSelectEntry.js │ │ │ │ │ ├── PaginationEntry.js │ │ │ │ │ ├── PathEntry.js │ │ │ │ │ ├── ReadonlyEntry.js │ │ │ │ │ ├── RepeatableEntry.js │ │ │ │ │ ├── RowCountEntry.js │ │ │ │ │ ├── SelectEntries.js │ │ │ │ │ ├── StaticColumnsSourceEntry.js │ │ │ │ │ ├── StaticOptionsSourceEntry.js │ │ │ │ │ ├── TableDataSourceEntry.js │ │ │ │ │ ├── TextEntry.js │ │ │ │ │ ├── ValueEntry.js │ │ │ │ │ ├── VersionTagEntry.js │ │ │ │ │ ├── factories │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── simpleBoolEntryFactory.js │ │ │ │ │ │ ├── simpleRangeIntegerEntryFactory.js │ │ │ │ │ │ ├── simpleSelectEntryFactory.js │ │ │ │ │ │ ├── simpleStringEntryFactory.js │ │ │ │ │ │ └── zeroPositiveIntegerEntryFactory.js │ │ │ │ │ └── index.js │ │ │ │ ├── groups │ │ │ │ │ ├── AppearanceGroup.js │ │ │ │ │ ├── ConditionGroup.js │ │ │ │ │ ├── ConstraintsGroup.js │ │ │ │ │ ├── CustomPropertiesGroup.js │ │ │ │ │ ├── GeneralGroup.js │ │ │ │ │ ├── LayoutGroup.js │ │ │ │ │ ├── OptionsGroups.js │ │ │ │ │ ├── SecurityAttributesGroup.js │ │ │ │ │ ├── SerializationGroup.js │ │ │ │ │ ├── TableHeaderGroups.js │ │ │ │ │ ├── ValidationGroup.js │ │ │ │ │ └── index.js │ │ │ │ ├── hooks │ │ │ │ │ ├── index.js │ │ │ │ │ ├── usePropertiesPanelService.js │ │ │ │ │ └── useVariables.js │ │ │ │ ├── icons │ │ │ │ │ ├── index.js │ │ │ │ │ ├── pp-checkbox-checked.svg │ │ │ │ │ ├── pp-checkbox.svg │ │ │ │ │ ├── pp-create.svg │ │ │ │ │ ├── pp-fullpanel-off.svg │ │ │ │ │ ├── pp-fullpanel.svg │ │ │ │ │ ├── pp-list-arrow.svg │ │ │ │ │ ├── pp-list-delete.svg │ │ │ │ │ └── pp-section-arrow.svg │ │ │ │ └── index.js │ │ │ ├── render-injection │ │ │ │ ├── RenderInjector.js │ │ │ │ ├── components │ │ │ │ │ └── InjectedRendersRoot.js │ │ │ │ ├── index.js │ │ │ │ └── slot-fill │ │ │ │ │ ├── Fill.js │ │ │ │ │ ├── FillContext.js │ │ │ │ │ ├── Slot.js │ │ │ │ │ ├── SlotContext.js │ │ │ │ │ ├── SlotFillRoot.js │ │ │ │ │ └── index.js │ │ │ ├── repeat-render │ │ │ │ ├── EditorRepeatRenderManager.js │ │ │ │ └── index.js │ │ │ └── selection │ │ │ │ ├── Selection.js │ │ │ │ ├── SelectionBehavior.js │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── render │ │ │ ├── EditorFormFields.js │ │ │ ├── Renderer.js │ │ │ ├── components │ │ │ │ ├── FieldDragPreview.js │ │ │ │ ├── FieldResizer.js │ │ │ │ ├── FormEditor.js │ │ │ │ ├── ModularSection.js │ │ │ │ ├── Util.js │ │ │ │ ├── editor-form-fields │ │ │ │ │ ├── EditorDocumentPreview.js │ │ │ │ │ ├── EditorExpressionField.js │ │ │ │ │ ├── EditorHtml.js │ │ │ │ │ ├── EditorIFrame.js │ │ │ │ │ ├── EditorTable.js │ │ │ │ │ ├── EditorText.js │ │ │ │ │ └── index.js │ │ │ │ └── icons │ │ │ │ │ ├── Close.svg │ │ │ │ │ ├── Delete.svg │ │ │ │ │ ├── Draggable.svg │ │ │ │ │ ├── EmptyForm.svg │ │ │ │ │ ├── Repeat.svg │ │ │ │ │ ├── Search.svg │ │ │ │ │ └── index.js │ │ │ ├── context │ │ │ │ ├── DragAndDropContext.js │ │ │ │ ├── FormEditorContext.js │ │ │ │ └── index.js │ │ │ ├── hooks │ │ │ │ ├── index.js │ │ │ │ ├── useDebounce.js │ │ │ │ ├── usePrevious.js │ │ │ │ └── useService.js │ │ │ ├── index.js │ │ │ └── util │ │ │ │ └── Cursor.js │ │ ├── types.d.ts │ │ └── types │ │ │ └── custom.d.ts │ └── test │ │ ├── TestHelper.js │ │ ├── coverageBundle.js │ │ ├── helper │ │ ├── index.js │ │ └── mocks │ │ │ └── index.js │ │ ├── spec │ │ ├── FormEditor.spec.js │ │ ├── complex.json │ │ ├── core │ │ │ ├── FieldFactory.spec.js │ │ │ ├── FormLayoutValidator.form.json │ │ │ └── FormLayoutValidator.spec.js │ │ ├── defaultValues.json │ │ ├── features │ │ │ ├── editor-actions │ │ │ │ └── FormEditorActions.spec.js │ │ │ ├── keyboard │ │ │ │ └── FormEditorKeyboardBindings.spec.js │ │ │ ├── modeling │ │ │ │ ├── FormLayoutUpdater.spec.js │ │ │ │ ├── Modeling.spec.js │ │ │ │ └── behavior │ │ │ │ │ ├── ColumnsSourceBehavior.spec.js │ │ │ │ │ ├── IdBehavior.spec.js │ │ │ │ │ ├── KeyBehavior.spec.js │ │ │ │ │ ├── OptionsSourceBehavior.spec.js │ │ │ │ │ ├── PathBehavior.spec.js │ │ │ │ │ └── ValidateBehavior.spec.js │ │ │ ├── palette │ │ │ │ ├── Palette.spec.js │ │ │ │ └── PaletteModule.spec.js │ │ │ ├── properties-panel │ │ │ │ ├── PropertiesPanel.spec.js │ │ │ │ ├── PropertiesPanelHeaderProvider.spec.js │ │ │ │ ├── PropertiesPanelModule.spec.js │ │ │ │ ├── groups │ │ │ │ │ ├── AppearanceGroup.spec.js │ │ │ │ │ ├── ConditionGroup.spec.js │ │ │ │ │ ├── GeneralGroup.spec.js │ │ │ │ │ ├── LayoutGroup.spec.js │ │ │ │ │ ├── SecurityAttributesGroup.spec.js │ │ │ │ │ ├── SerializationGroup.spec.js │ │ │ │ │ └── ValidationGroup.spec.js │ │ │ │ └── helper │ │ │ │ │ ├── index.js │ │ │ │ │ └── mocks │ │ │ │ │ └── index.js │ │ │ ├── render-injection │ │ │ │ ├── RenderInjector.spec.js │ │ │ │ └── SlotFill.spec.js │ │ │ └── selection │ │ │ │ ├── Selection.spec.js │ │ │ │ └── SelectionBehavior.spec.js │ │ ├── form-group.json │ │ ├── form-no-ids.json │ │ ├── form-rows.json │ │ ├── form-table.json │ │ ├── form.json │ │ ├── import │ │ │ └── Importer.spec.js │ │ ├── other.json │ │ ├── redundantValues.json │ │ └── render │ │ │ └── components │ │ │ └── form-fields │ │ │ ├── EditorText.spec.js │ │ │ └── helper │ │ │ └── index.js │ │ ├── test.css │ │ └── testBundle.js ├── form-js-integration │ ├── package.json │ └── src │ │ └── application.ts ├── form-js-playground │ ├── LICENSE │ ├── README.md │ ├── karma.conf.js │ ├── package.json │ ├── resources │ │ └── screenshot.png │ ├── rollup.config.js │ ├── src │ │ ├── Playground.js │ │ ├── components │ │ │ ├── EmbedModal.js │ │ │ ├── FileDrop.css │ │ │ ├── JSONEditor.js │ │ │ ├── Modal.js │ │ │ ├── PlaygroundRoot.css │ │ │ ├── PlaygroundRoot.js │ │ │ ├── Section.js │ │ │ └── autocompletion │ │ │ │ ├── VariablesFacet.js │ │ │ │ └── index.js │ │ └── index.js │ └── test │ │ ├── TestHelper.js │ │ ├── coverageBundle.js │ │ ├── custom │ │ ├── editor.js │ │ ├── range.svg │ │ ├── styles.css │ │ └── viewer.js │ │ ├── spec │ │ ├── JSONEditor.spec.js │ │ ├── Playground.spec.js │ │ ├── custom.json │ │ ├── form.json │ │ ├── other-form.json │ │ └── rows-form.json │ │ ├── test.css │ │ └── testBundle.js ├── form-js-viewer │ ├── LICENSE │ ├── README.md │ ├── assets │ │ ├── form-js-base.css │ │ ├── grid.scss │ │ └── index.scss │ ├── karma.conf.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── Form.js │ │ ├── core │ │ │ ├── EventBus.js │ │ │ ├── FieldFactory.js │ │ │ ├── FormFieldInstanceRegistry.js │ │ │ ├── FormFieldRegistry.js │ │ │ ├── FormLayouter.js │ │ │ ├── Importer.js │ │ │ ├── PathRegistry.js │ │ │ ├── Validator.js │ │ │ └── index.js │ │ ├── features │ │ │ ├── expressionField │ │ │ │ ├── ExpressionLoopPreventer.js │ │ │ │ └── index.js │ │ │ ├── expressionLanguage │ │ │ │ ├── ConditionChecker.js │ │ │ │ ├── FeelExpressionLanguage.js │ │ │ │ ├── FeelersTemplating.js │ │ │ │ ├── index.js │ │ │ │ └── variableExtractionHelpers.js │ │ │ ├── index.js │ │ │ ├── markdown │ │ │ │ ├── MarkdownRenderer.js │ │ │ │ └── index.js │ │ │ ├── repeatRender │ │ │ │ ├── RepeatRenderManager.js │ │ │ │ └── index.js │ │ │ └── viewerCommands │ │ │ │ ├── ViewerCommands.js │ │ │ │ ├── cmd │ │ │ │ ├── UpdateFieldInstanceValidationHandler.js │ │ │ │ └── UpdateFieldValidationHandler.js │ │ │ │ └── index.js │ │ ├── index.js │ │ ├── render │ │ │ ├── FileRegistry.js │ │ │ ├── FormFields.js │ │ │ ├── Renderer.js │ │ │ ├── components │ │ │ │ ├── .DS_Store │ │ │ │ ├── Description.js │ │ │ │ ├── Errors.js │ │ │ │ ├── FormComponent.js │ │ │ │ ├── FormField.js │ │ │ │ ├── Label.js │ │ │ │ ├── PoweredBy.js │ │ │ │ ├── Sanitizer.js │ │ │ │ ├── Util.js │ │ │ │ ├── form-fields │ │ │ │ │ ├── Button.js │ │ │ │ │ ├── Checkbox.js │ │ │ │ │ ├── Checklist.js │ │ │ │ │ ├── Datetime.js │ │ │ │ │ ├── Default.js │ │ │ │ │ ├── DocumentPreview.js │ │ │ │ │ ├── DynamicList.js │ │ │ │ │ ├── ExpressionField.js │ │ │ │ │ ├── FilePicker.js │ │ │ │ │ ├── Group.js │ │ │ │ │ ├── Html.js │ │ │ │ │ ├── IFrame.js │ │ │ │ │ ├── Image.js │ │ │ │ │ ├── Number.js │ │ │ │ │ ├── Radio.js │ │ │ │ │ ├── Select.js │ │ │ │ │ ├── Separator.js │ │ │ │ │ ├── Spacer.js │ │ │ │ │ ├── Table.js │ │ │ │ │ ├── Taglist.js │ │ │ │ │ ├── Text.js │ │ │ │ │ ├── Textarea.js │ │ │ │ │ ├── Textfield.js │ │ │ │ │ ├── icons │ │ │ │ │ │ ├── Add.svg │ │ │ │ │ │ ├── AngelDown.svg │ │ │ │ │ │ ├── AngelUp.svg │ │ │ │ │ │ ├── ArrowDown.svg │ │ │ │ │ │ ├── ArrowUp.svg │ │ │ │ │ │ ├── Calendar.svg │ │ │ │ │ │ ├── CaretLeft.svg │ │ │ │ │ │ ├── CaretRight.svg │ │ │ │ │ │ ├── Clock.svg │ │ │ │ │ │ ├── Collapse.svg │ │ │ │ │ │ ├── Delete.svg │ │ │ │ │ │ ├── Download.svg │ │ │ │ │ │ ├── Expand.svg │ │ │ │ │ │ ├── ImagePlaceholder.svg │ │ │ │ │ │ └── XMark.svg │ │ │ │ │ └── parts │ │ │ │ │ │ ├── ChildrenRenderer.js │ │ │ │ │ │ ├── Datepicker.js │ │ │ │ │ │ ├── DropdownList.js │ │ │ │ │ │ ├── InputAdorner.js │ │ │ │ │ │ ├── SearchableSelect.js │ │ │ │ │ │ ├── SimpleSelect.js │ │ │ │ │ │ ├── SkipLink.js │ │ │ │ │ │ ├── TemplatedInputAdorner.js │ │ │ │ │ │ └── Timepicker.js │ │ │ │ ├── icons │ │ │ │ │ ├── Button.svg │ │ │ │ │ ├── Checkbox.svg │ │ │ │ │ ├── Checklist.svg │ │ │ │ │ ├── Datetime.svg │ │ │ │ │ ├── DocumentPreview.svg │ │ │ │ │ ├── DynamicList.svg │ │ │ │ │ ├── ExpressionField.svg │ │ │ │ │ ├── FilePicker.svg │ │ │ │ │ ├── Form.svg │ │ │ │ │ ├── Group.svg │ │ │ │ │ ├── HTML.svg │ │ │ │ │ ├── IFrame.svg │ │ │ │ │ ├── Image.svg │ │ │ │ │ ├── Number.svg │ │ │ │ │ ├── Radio.svg │ │ │ │ │ ├── Select.svg │ │ │ │ │ ├── Separator.svg │ │ │ │ │ ├── Spacer.svg │ │ │ │ │ ├── Table.svg │ │ │ │ │ ├── Taglist.svg │ │ │ │ │ ├── Text.svg │ │ │ │ │ ├── Textarea.svg │ │ │ │ │ ├── Textfield.svg │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── util │ │ │ │ │ ├── dateTimeUtil.js │ │ │ │ │ ├── domUtil.js │ │ │ │ │ ├── localisationUtil.js │ │ │ │ │ ├── numberFieldUtil.js │ │ │ │ │ ├── optionsUtil.js │ │ │ │ │ └── sanitizerUtil.js │ │ │ ├── context │ │ │ │ ├── FormContext.js │ │ │ │ ├── FormRenderContext.js │ │ │ │ ├── LocalExpressionContext.js │ │ │ │ └── index.js │ │ │ ├── hooks │ │ │ │ ├── index.js │ │ │ │ ├── useBooleanExpressionEvaluation.js │ │ │ │ ├── useCleanupMultiSelectValue.js │ │ │ │ ├── useCleanupSingleSelectValue.js │ │ │ │ ├── useCondition.js │ │ │ │ ├── useDangerousHTMLWrapper.js │ │ │ │ ├── useDeepCompareMemoize.js │ │ │ │ ├── useExpressionEvaluation.js │ │ │ │ ├── useFilteredFormData.js │ │ │ │ ├── useFlushDebounce.js │ │ │ │ ├── useGetLabelCorrelation.js │ │ │ │ ├── useKeyDownAction.js │ │ │ │ ├── useOptionsAsync.js │ │ │ │ ├── usePrevious.js │ │ │ │ ├── useReadonly.js │ │ │ │ ├── useScrollIntoView.js │ │ │ │ ├── useSecurityAttributesMap.js │ │ │ │ ├── useService.js │ │ │ │ ├── useSingleLineTemplateEvaluation.js │ │ │ │ └── useTemplateEvaluation.js │ │ │ └── index.js │ │ ├── types.d.ts │ │ └── util │ │ │ ├── constants │ │ │ ├── DatetimeConstants.js │ │ │ ├── FilePickerConstants.js │ │ │ ├── IFrameConstants.js │ │ │ ├── OptionsSourceConstants.js │ │ │ └── index.js │ │ │ ├── expressions.js │ │ │ ├── extractFileReferencesFromRemovedData.js │ │ │ ├── form.js │ │ │ ├── getSchemaVariables.js │ │ │ ├── index.js │ │ │ ├── injector.js │ │ │ ├── simple.js │ │ │ └── structure.js │ └── test │ │ ├── TestHelper.js │ │ ├── coverageBundle.js │ │ ├── helper │ │ ├── index.js │ │ └── preactDebuggers.js │ │ ├── spec │ │ ├── Form.spec.js │ │ ├── appearance.json │ │ ├── chain-expressions.json │ │ ├── complex-conditions.json │ │ ├── complex-expressions.json │ │ ├── condition-errors-dynamic-list.json │ │ ├── condition-errors.json │ │ ├── condition-external-variable.json │ │ ├── condition.json │ │ ├── core │ │ │ ├── FormFieldInstanceRegistry.spec.js │ │ │ ├── FormFieldRegistry.spec.js │ │ │ ├── FormLayouter.spec.js │ │ │ ├── PathRegistry.spec.js │ │ │ └── Validator.spec.js │ │ ├── custom │ │ │ ├── custom.css │ │ │ └── index.js │ │ ├── customField.json │ │ ├── cyclical-expressions.json │ │ ├── defaultValues.json │ │ ├── descriptions.json │ │ ├── disabled.json │ │ ├── documentPreview.json │ │ ├── dynamic-list-table-filter-interaction.json │ │ ├── dynamic-list-variables.json │ │ ├── dynamic.json │ │ ├── expression-external-variable.json │ │ ├── expressionField.json │ │ ├── features │ │ │ └── expression-language │ │ │ │ ├── ConditionChecker.spec.js │ │ │ │ ├── FeelExpressionLanguage.spec.js │ │ │ │ └── variableExtractionHelpers.spec.js │ │ ├── filepicker.json │ │ ├── focusables.json │ │ ├── form.json │ │ ├── groups.json │ │ ├── hidden-fields-conditional.json │ │ ├── hidden-fields-expression.json │ │ ├── html.json │ │ ├── iframes.json │ │ ├── images.json │ │ ├── import │ │ │ └── Importer.spec.js │ │ ├── labels.json │ │ ├── nested-complex-context.json │ │ ├── other.json │ │ ├── radio-tabbing.json │ │ ├── readonly-expression.json │ │ ├── render │ │ │ ├── components │ │ │ │ ├── Description.spec.js │ │ │ │ ├── FormField.spec.js │ │ │ │ ├── Label.spec.js │ │ │ │ ├── Sanitizer.spec.js │ │ │ │ ├── Util.spec.js │ │ │ │ ├── form-fields │ │ │ │ │ ├── Button.spec.js │ │ │ │ │ ├── Checkbox.spec.js │ │ │ │ │ ├── Checklist.spec.js │ │ │ │ │ ├── Datetime.spec.js │ │ │ │ │ ├── Default.spec.js │ │ │ │ │ ├── DocumentPreview.spec.js │ │ │ │ │ ├── Dynamiclist.spec.js │ │ │ │ │ ├── ExpressionField.spec.js │ │ │ │ │ ├── FilePicker.spec.js │ │ │ │ │ ├── Group.spec.js │ │ │ │ │ ├── Html.spec.js │ │ │ │ │ ├── IFrame.spec.js │ │ │ │ │ ├── Image.spec.js │ │ │ │ │ ├── Number.spec.js │ │ │ │ │ ├── Radio.spec.js │ │ │ │ │ ├── Select.spec.js │ │ │ │ │ ├── Separator.spec.js │ │ │ │ │ ├── Spacer.spec.js │ │ │ │ │ ├── Table.spec.js │ │ │ │ │ ├── Taglist.spec.js │ │ │ │ │ ├── Text.spec.js │ │ │ │ │ ├── Textarea.spec.js │ │ │ │ │ ├── Textfield.spec.js │ │ │ │ │ ├── parts │ │ │ │ │ │ └── DropdownList.spec.js │ │ │ │ │ └── util │ │ │ │ │ │ ├── numberFieldUtil.spec.js │ │ │ │ │ │ ├── optionsUtil.spec.js │ │ │ │ │ │ └── sanitizerUtil.spec.js │ │ │ │ ├── helper │ │ │ │ │ ├── index.js │ │ │ │ │ └── mocks │ │ │ │ │ │ └── index.js │ │ │ │ └── util │ │ │ │ │ ├── DateTimeUtil.spec.js │ │ │ │ │ ├── domUtil.spec.js │ │ │ │ │ └── localisationUtil.spec.js │ │ │ └── hooks │ │ │ │ ├── useCleanupMultiSelectValues.spec.js │ │ │ │ ├── useCleanupSingleSelectValue.spec.js │ │ │ │ └── useKeyDownAction.spec.js │ │ ├── required.json │ │ ├── rows.json │ │ ├── ships-example.json │ │ ├── stress.json │ │ ├── template-variable-complex.json │ │ ├── template-variable.json │ │ ├── text-template.json │ │ ├── text.json │ │ ├── util │ │ │ └── GetSchemaVariables.spec.js │ │ ├── validate.json │ │ └── valuesExpression.json │ │ ├── test.css │ │ ├── testBundle.js │ │ └── theme.scss ├── form-js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── editor.js │ │ ├── index.js │ │ ├── playground.js │ │ └── viewer.js │ └── test │ │ ├── TestHelper.js │ │ ├── config │ │ ├── karma.distro.js │ │ └── karma.unit.js │ │ ├── coverageBundle.js │ │ ├── distro │ │ ├── form-editor.js │ │ ├── form-playground.js │ │ └── form-viewer.js │ │ ├── spec │ │ ├── Form.spec.js │ │ ├── FormEditor.spec.js │ │ ├── FormPlayground.spec.js │ │ └── form.json │ │ ├── test.css │ │ └── testBundle.js └── form-json-schema │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ ├── defs │ │ ├── appearance.json │ │ ├── columns.json │ │ ├── component.json │ │ ├── conditional.json │ │ ├── examples │ │ │ └── components.json │ │ ├── exporter.json │ │ ├── field-types │ │ │ ├── containers.json │ │ │ ├── inputs.json │ │ │ ├── multi-inputs.json │ │ │ └── presentation-components.json │ │ ├── layout.json │ │ ├── properties.json │ │ ├── rules │ │ │ ├── rules-allowed-properties.json │ │ │ ├── rules-default.json │ │ │ ├── rules-defaultValue-type.json │ │ │ └── rules-required-properties.json │ │ ├── security.json │ │ ├── type.json │ │ ├── validate.json │ │ └── values.json │ ├── error-messages.json │ └── index.json │ ├── tasks │ ├── generate-error-messages.js │ └── generate-schema.js │ └── test │ ├── fixtures │ ├── accept-not-allowed.js │ ├── action-not-allowed.js │ ├── alt-not-allowed.js │ ├── appearance-prefixAdorner-not-allowed.js │ ├── appearance-suffixAdorner-not-allowed.js │ ├── columns-columnsExpression-exclusive.js │ ├── columns-not-allowed.js │ ├── columnsExpression-not-allowed.js │ ├── complex.js │ ├── components-not-allowed.js │ ├── conditional-not-allowed.js │ ├── content-not-allowed.js │ ├── dataSource-not-allowed.js │ ├── dateLabel-not-allowed.js │ ├── decimalDigits-not-allowed.js │ ├── defaultValue-no-array.js │ ├── defaultValue-no-boolean.js │ ├── defaultValue-no-string-or-number.js │ ├── defaultValue-no-string.js │ ├── defaultValue-not-allowed.js │ ├── description-not-allowed.js │ ├── disabled-not-allowed.js │ ├── disallowPassedDates-not-allowed.js │ ├── documentPreview.js │ ├── dynamic-list-properties-not-allowed.js │ ├── dynamic-list-properties.js │ ├── dynamiclists.js │ ├── expression-field-expression-required.js │ ├── expression-properties.js │ ├── filepicker.js │ ├── groups.js │ ├── height-not-allowed.js │ ├── iframe.js │ ├── increment-not-allowed.js │ ├── increment.js │ ├── key-invalid.js │ ├── key-not-allowed.js │ ├── label-not-allowed.js │ ├── layout-empty-row.js │ ├── layout-not-allowed.js │ ├── maxHeight-not-allowed.js │ ├── multiple-not-allowed.js │ ├── no-action.js │ ├── no-key.js │ ├── no-subtype.js │ ├── no-values-property.js │ ├── package-lock.json │ ├── package.json │ ├── path-invalid.js │ ├── path-not-allowed.js │ ├── properties-not-allowed.js │ ├── readonly-not-allowed.js │ ├── rowCount-no-number.js │ ├── rowCount-not-allowed.js │ ├── schemaVersion-not-supported.js │ ├── searchable-not-allowed.js │ ├── serializeToString-not-allowed.js │ ├── showOutline-not-allowed.js │ ├── simple.js │ ├── source-not-allowed.js │ ├── subtype-not-allowed.js │ ├── text-not-allowed.js │ ├── timeInterval-not-allowed.js │ ├── timeLabel-not-allowed.js │ ├── timeSerializingFormat-not-allowed.js │ ├── use24h-not-allowed.js │ ├── validate-max-not-allowed.js │ ├── validate-maxLength-not-allowed.js │ ├── validate-min-not-allowed.js │ ├── validate-minLength-not-allowed.js │ ├── validate-not-allowed.js │ ├── validate-pattern-not-allowed.js │ ├── validate-validationType-not-allowed.js │ ├── validate-validationType.js │ ├── values-not-allowed.js │ ├── valuesExpression-not-allowed.js │ ├── valuesKey-invalid.js │ ├── valuesKey-not-allowed.js │ ├── verticalAlignment-invalid.js │ ├── verticalAlignment-not-allowed.js │ └── verticalAlignment.js │ ├── helpers │ └── index.js │ └── spec │ ├── schema.spec.js │ └── validation.spec.js ├── playwright.config.js ├── renovate.json ├── tasks └── stages │ ├── await-published │ ├── update-demo │ ├── update-examples │ └── update-website ├── tsconfig.json └── vite.config.js /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | 3 | parsers: 4 | javascript: 5 | enable_partials: yes 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | Closes # 11 | 12 | - [ ] This PR adds a new `form-js` element or visually changes an existing component. 13 | - => In that case, we need to ensure we follow up on this, e.g. by [creating an issue in Tasklist](https://github.com/camunda/tasklist/issues/new/choose) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | node_modules 4 | .vscode 5 | test-results/ 6 | playwright-report/ 7 | playwright/.cache/ 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "bracketSameLine": true, 6 | "endOfLine": "lf" 7 | } 8 | -------------------------------------------------------------------------------- /e2e/README.md: -------------------------------------------------------------------------------- 1 | # E2E Tests 2 | 3 | ## Visual regression 4 | 5 | To run the visual regression tests follow the steps below: 6 | 7 | 1. Bootstrap the project with `npm ci` and `npm run build` 8 | 2. Run `npm run start:container` 9 | 3. Build the mock website `npm run build:e2e` 10 | 4. Start the local server with `npm run start:visual-preview` 11 | 5. Run the visual regression tests with `npm run test:visual` 12 | 13 | **_If you need to update the screenshots run `npm run test:visual -- --update-snapshots`_** 14 | 15 | **_If you're using Apple silicon and get an error with ESBuild run `npm i vite --force --no-save` inside the Docker container after step 2 to fix it_** 16 | -------------------------------------------------------------------------------- /e2e/carbon/carbon.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/styles' as * with ( 2 | $font-path: '@ibm/plex' 3 | ); 4 | @use '@carbon/styles/scss/themes'; 5 | @use '@carbon/styles/scss/theme'; 6 | 7 | @import '@bpmn-io/form-js-carbon-styles/src/carbon-styles'; 8 | 9 | [data-carbon-theme='g10'] { 10 | @include theme.theme(themes.$g10); 11 | } 12 | 13 | [data-carbon-theme='g100'] { 14 | @include theme.theme(themes.$g100); 15 | } 16 | -------------------------------------------------------------------------------- /e2e/carbon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Carbon styles 8 | 11 | 12 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /e2e/carbon/index.js: -------------------------------------------------------------------------------- 1 | import '@bpmn-io/form-js/dist/assets/form-js.css'; 2 | import '@bpmn-io/form-js/dist/assets/form-js-editor.css'; 3 | import '@bpmn-io/form-js/dist/assets/form-js-playground.css'; 4 | 5 | import './carbon.scss'; 6 | 7 | import { renderSchema } from '../renderSchema'; 8 | 9 | renderSchema(); 10 | -------------------------------------------------------------------------------- /e2e/carbon/theme.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/styles' as * with ( 2 | $font-path: '@ibm/plex' 3 | ); 4 | @use '@carbon/styles/scss/themes'; 5 | @use '@carbon/styles/scss/theme'; 6 | 7 | [data-carbon-theme='g10'] { 8 | @include theme.theme(themes.$g10); 9 | } 10 | 11 | [data-carbon-theme='g100'] { 12 | @include theme.theme(themes.$g100); 13 | } 14 | -------------------------------------------------------------------------------- /e2e/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Forms playground 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /e2e/main.js: -------------------------------------------------------------------------------- 1 | import '@bpmn-io/form-js/dist/assets/form-js.css'; 2 | import '@bpmn-io/form-js/dist/assets/form-js-editor.css'; 3 | import '@bpmn-io/form-js/dist/assets/form-js-playground.css'; 4 | 5 | import { renderSchema } from './renderSchema'; 6 | 7 | renderSchema(); 8 | -------------------------------------------------------------------------------- /e2e/styles.scss: -------------------------------------------------------------------------------- 1 | $font-prefix: '../node_modules/@ibm/plex'; 2 | @import '../node_modules/@ibm/plex/scss/ibm-plex.scss'; 3 | 4 | html, 5 | body, 6 | #container { 7 | margin: 0; 8 | padding: 0; 9 | width: 100vw; 10 | height: 100vh; 11 | } 12 | -------------------------------------------------------------------------------- /e2e/test-fixtures.js: -------------------------------------------------------------------------------- 1 | import { test as base } from '@playwright/test'; 2 | import AxeBuilder from '@axe-core/playwright'; 3 | 4 | const DEFAULT_AXE_TAGS = ['best-practice', 'wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'cat.semantics', 'cat.forms']; 5 | 6 | const DEFAULT_DISABLE_RULES = ['page-has-heading-one']; 7 | 8 | const test = base.extend({ 9 | makeAxeBuilder: async ({ page }, use) => { 10 | const makeAxeBuilder = (options = {}) => { 11 | const { tags = DEFAULT_AXE_TAGS, disableRules = DEFAULT_DISABLE_RULES } = options; 12 | 13 | return new AxeBuilder({ page }).withTags(tags).disableRules(disableRules); 14 | }; 15 | 16 | await use(makeAxeBuilder); 17 | }, 18 | }); 19 | 20 | export { test }; 21 | -------------------------------------------------------------------------------- /e2e/theming/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Theming 8 | 11 | 12 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /e2e/theming/index.js: -------------------------------------------------------------------------------- 1 | import '@bpmn-io/form-js/dist/assets/form-js.css'; 2 | import '@bpmn-io/form-js/dist/assets/form-js-editor.css'; 3 | import '@bpmn-io/form-js/dist/assets/form-js-playground.css'; 4 | 5 | import './theme.scss'; 6 | 7 | import { renderSchema } from '../renderSchema'; 8 | 9 | renderSchema(); 10 | -------------------------------------------------------------------------------- /e2e/theming/theme.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/styles' as * with ( 2 | $font-path: '@ibm/plex' 3 | ); 4 | @use '@carbon/styles/scss/themes'; 5 | @use '@carbon/styles/scss/theme'; 6 | 7 | [data-carbon-theme='g10'] { 8 | @include theme.theme(themes.$g10); 9 | } 10 | 11 | [data-carbon-theme='g100'] { 12 | @include theme.theme(themes.$g100); 13 | } 14 | -------------------------------------------------------------------------------- /e2e/visual/carbon-styles.spec.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('@playwright/test'); 2 | 3 | const { test } = require('../test-fixtures'); 4 | 5 | const schema = require('./fixtures/complex.json'); 6 | 7 | test('carbon styles', async ({ page, makeAxeBuilder }) => { 8 | // given 9 | await page.route('/form', (route) => { 10 | route.fulfill({ 11 | status: 200, 12 | body: JSON.stringify({ 13 | data: { 14 | schema, 15 | component: 'viewer', 16 | }, 17 | }), 18 | }); 19 | }); 20 | 21 | // when 22 | await page.goto('/carbon/'); 23 | 24 | await page.waitForSelector('#container', { 25 | state: 'visible', 26 | }); 27 | 28 | // then 29 | await expect(page).toHaveScreenshot(); 30 | 31 | // and then 32 | const results = await makeAxeBuilder().analyze(); 33 | 34 | expect(results.violations).toHaveLength(0); 35 | expect(results.passes.length).toBeGreaterThan(0); 36 | }); 37 | -------------------------------------------------------------------------------- /e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/carbon-styles.spec.js-snapshots/carbon-styles-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/empty-ui.spec.js: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | import schema from './fixtures/empty.json'; 4 | 5 | test('empty playground', async ({ page }) => { 6 | // given 7 | await page.route('/form', (route) => { 8 | route.fulfill({ 9 | status: 200, 10 | body: JSON.stringify({ 11 | data: { 12 | schema, 13 | component: 'playground', 14 | }, 15 | }), 16 | }); 17 | }); 18 | 19 | // when 20 | await page.goto('/'); 21 | 22 | // then 23 | await expect(page).toHaveScreenshot(); 24 | }); 25 | -------------------------------------------------------------------------------- /e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/empty-ui.spec.js-snapshots/empty-playground-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/fixtures/empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../packages/form-json-schema/resources/schema.json", 3 | "schemaVersion": 8, 4 | "exporter": { 5 | "name": "Camunda Web Modeler", 6 | "version": "0de37f6" 7 | }, 8 | "components": [], 9 | "type": "default", 10 | "id": "form_id", 11 | "executionPlatform": "Camunda Cloud", 12 | "executionPlatformVersion": "8.2.0" 13 | } 14 | -------------------------------------------------------------------------------- /e2e/visual/groups-ui.spec.js: -------------------------------------------------------------------------------- 1 | import { test, expect } from '@playwright/test'; 2 | 3 | import schema from './fixtures/groups.json'; 4 | 5 | test('groups playground', async ({ page }) => { 6 | // given 7 | await page.route('/form', (route) => { 8 | route.fulfill({ 9 | status: 200, 10 | body: JSON.stringify({ 11 | data: { 12 | schema, 13 | component: 'playground', 14 | }, 15 | }), 16 | }); 17 | }); 18 | 19 | // when 20 | await page.goto('/'); 21 | 22 | // then 23 | await expect(page).toHaveScreenshot(); 24 | }); 25 | -------------------------------------------------------------------------------- /e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/groups-ui.spec.js-snapshots/groups-playground-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---editor-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/no-theme.spec.js-snapshots/no-theme---viewer-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---editor-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---editor-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---editor-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---editor-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---editor-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---editor-1-webkit-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---viewer-1-chromium-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---viewer-1-chromium-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---viewer-1-firefox-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---viewer-1-firefox-linux.png -------------------------------------------------------------------------------- /e2e/visual/theming.spec.js-snapshots/theming---viewer-1-webkit-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/e2e/visual/theming.spec.js-snapshots/theming---viewer-1-webkit-linux.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.15.2", 3 | "$schema": "node_modules/lerna/schemas/lerna-schema.json" 4 | } 5 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bpmn-io/form-js-carbon-styles", 3 | "version": "1.15.2", 4 | "description": "Custom carbon styles for form-js", 5 | "scripts": { 6 | "all": "run-s test", 7 | "test": "karma start", 8 | "dev": "npm test -- --auto-watch --no-single-run" 9 | }, 10 | "license": "SEE LICENSE IN LICENSE", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/bpmn-io/form-js.git", 14 | "directory": "packages/form-js-carbon-styles" 15 | }, 16 | "author": { 17 | "name": "bpmn.io contributors", 18 | "url": "https://github.com/bpmn-io" 19 | }, 20 | "devDependencies": { 21 | "@bpmn-io/form-js-viewer": "^1.15.2", 22 | "@carbon/elements": "^11.63.2", 23 | "styled-components": "^6.1.16" 24 | }, 25 | "files": [ 26 | "src" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/src/types/carbon-styles.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * DEPRECATED: This file is deprecated and will be removed with one of the next releases. 3 | */ 4 | 5 | declare module '@bpmn-io/form-js-carbon-styles' { 6 | import { FlattenSimpleInterpolation, GlobalStyleComponent, DefaultTheme } from 'styled-components'; 7 | 8 | const CARBON_STYLES: FlattenSimpleInterpolation; 9 | const GlobalFormStyling: GlobalStyleComponent<{}, DefaultTheme>; 10 | } 11 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/test/TestHelper.js: -------------------------------------------------------------------------------- 1 | export function isSingleStart(topic) { 2 | return window.__env__ && window.__env__.SINGLE_START === topic; 3 | } 4 | 5 | export function insertCSS(name, css) { 6 | if (document.querySelector('[data-css-file="' + name + '"]')) { 7 | return; 8 | } 9 | 10 | const head = document.head || document.getElementsByTagName('head')[0]; 11 | const style = document.createElement('style'); 12 | style.setAttribute('data-css-file', name); 13 | 14 | style.type = 'text/css'; 15 | style.appendChild(document.createTextNode(css)); 16 | 17 | head.appendChild(style); 18 | } 19 | 20 | export { expectNoViolations } from '../../form-js-viewer/test/helper'; 21 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/test/spec/theme.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/react' as * with ( 2 | $font-path: '@ibm/plex' 3 | ); 4 | 5 | @use '@carbon/react/scss/themes'; 6 | @use '@carbon/react/scss/theme'; 7 | 8 | [data-carbon-theme='g10'] { 9 | @include theme.theme(themes.$g10); 10 | } 11 | 12 | [data-carbon-theme='g100'] { 13 | @include theme.theme(themes.$g100); 14 | } 15 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/test/test.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | font-size: 16px; 4 | margin: 1rem; 5 | } 6 | 7 | h1 { 8 | margin: 0 0 1rem 0; 9 | } 10 | 11 | .form-container { 12 | padding: 20px; 13 | } 14 | 15 | .test-container { 16 | height: 100% !important; 17 | } 18 | 19 | .test-content-container { 20 | overflow: auto !important; 21 | } 22 | -------------------------------------------------------------------------------- /packages/form-js-carbon-styles/test/testBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/assets/index.scss: -------------------------------------------------------------------------------- 1 | @use 'form-js-editor-base.css'; 2 | @use '@bpmn-io/draggle/dist/draggle.css'; 3 | @use '@bpmn-io/properties-panel/dist/assets/properties-panel.css'; 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/core/Debounce.js: -------------------------------------------------------------------------------- 1 | import { debounce } from 'min-dash'; 2 | 3 | /** 4 | * A factory to create a configurable debouncer. 5 | * 6 | * @param {number|boolean} [config=true] 7 | */ 8 | export function DebounceFactory(config = true) { 9 | const timeout = typeof config === 'number' ? config : config ? 300 : 0; 10 | 11 | if (timeout) { 12 | return (fn) => debounce(fn, timeout); 13 | } else { 14 | return (fn) => fn; 15 | } 16 | } 17 | 18 | DebounceFactory.$inject = ['config.debounce']; 19 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/core/EventBus.js: -------------------------------------------------------------------------------- 1 | export { default as EventBus } from 'diagram-js/lib/core/EventBus'; 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/core/FormLayouter.js: -------------------------------------------------------------------------------- 1 | export { FormLayouter } from '@bpmn-io/form-js-viewer'; 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/core/index.js: -------------------------------------------------------------------------------- 1 | import { FieldFactory, Importer, PathRegistry } from '@bpmn-io/form-js-viewer'; 2 | 3 | import { EventBus } from './EventBus'; 4 | import { DebounceFactory } from './Debounce'; 5 | import { FormFieldRegistry } from './FormFieldRegistry'; 6 | import { FormLayouter } from './FormLayouter'; 7 | import { FormLayoutValidator } from './FormLayoutValidator'; 8 | 9 | import { RenderModule } from '../render'; 10 | 11 | export const CoreModule = { 12 | __depends__: [RenderModule], 13 | debounce: ['factory', DebounceFactory], 14 | eventBus: ['type', EventBus], 15 | importer: ['type', Importer], 16 | formFieldRegistry: ['type', FormFieldRegistry], 17 | pathRegistry: ['type', PathRegistry], 18 | formLayouter: ['type', FormLayouter], 19 | formLayoutValidator: ['type', FormLayoutValidator], 20 | fieldFactory: ['type', FieldFactory], 21 | }; 22 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/dragging/index.js: -------------------------------------------------------------------------------- 1 | import { Dragging } from './Dragging'; 2 | 3 | export const DraggingModule = { 4 | __init__: ['dragging'], 5 | dragging: ['type', Dragging], 6 | }; 7 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/editor-actions/index.js: -------------------------------------------------------------------------------- 1 | import BaseEditorActionsModule from 'diagram-js/lib/features/editor-actions'; 2 | 3 | import { FormEditorActions } from './FormEditorActions'; 4 | 5 | export const EditorActionsModule = { 6 | __depends__: [BaseEditorActionsModule], 7 | editorActions: ['type', FormEditorActions], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/expression-language/EditorTemplating.js: -------------------------------------------------------------------------------- 1 | import { isString } from 'min-dash'; 2 | 3 | export class EditorTemplating { 4 | // same rules as viewer templating 5 | isTemplate(value) { 6 | return isString(value) && (value.startsWith('=') || /{{/.test(value)); 7 | } 8 | 9 | // return the template raw, as we usually just want to display that 10 | evaluate(template) { 11 | return template; 12 | } 13 | } 14 | 15 | EditorTemplating.$inject = []; 16 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/expression-language/index.js: -------------------------------------------------------------------------------- 1 | import { FeelExpressionLanguage } from '@bpmn-io/form-js-viewer'; 2 | import { EditorTemplating } from './EditorTemplating'; 3 | 4 | export const EditorExpressionLanguageModule = { 5 | __init__: ['expressionLanguage', 'templating'], 6 | expressionLanguage: ['type', FeelExpressionLanguage], 7 | templating: ['type', EditorTemplating], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/keyboard/index.js: -------------------------------------------------------------------------------- 1 | import KeyboardModule from 'diagram-js/lib/features/keyboard'; 2 | 3 | import { FormEditorKeyboardBindings } from './FormEditorKeyboardBindings'; 4 | 5 | export const FormEditorKeyboardModule = { 6 | __depends__: [KeyboardModule], 7 | __init__: ['keyboardBindings'], 8 | keyboardBindings: ['type', FormEditorKeyboardBindings], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/modeling/behavior/IdBehavior.js: -------------------------------------------------------------------------------- 1 | import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; 2 | 3 | export class IdBehavior extends CommandInterceptor { 4 | constructor(eventBus, modeling) { 5 | super(eventBus); 6 | 7 | this.preExecute( 8 | 'formField.remove', 9 | function (context) { 10 | const { formField } = context; 11 | 12 | const { id } = formField; 13 | 14 | modeling.unclaimId(formField, id); 15 | }, 16 | true, 17 | ); 18 | 19 | this.preExecute( 20 | 'formField.edit', 21 | function (context) { 22 | const { formField, properties } = context; 23 | 24 | if ('id' in properties) { 25 | modeling.unclaimId(formField, formField.id); 26 | 27 | modeling.claimId(formField, properties.id); 28 | } 29 | }, 30 | true, 31 | ); 32 | } 33 | } 34 | 35 | IdBehavior.$inject = ['eventBus', 'modeling']; 36 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/modeling/behavior/TableDataSourceBehavior.js: -------------------------------------------------------------------------------- 1 | import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; 2 | 3 | import { get } from 'min-dash'; 4 | 5 | export class TableDataSourceBehavior extends CommandInterceptor { 6 | constructor(eventBus) { 7 | super(eventBus); 8 | 9 | this.preExecute( 10 | 'formField.add', 11 | function (context) { 12 | const { formField } = context; 13 | 14 | if (get(formField, ['type']) !== 'table') { 15 | return; 16 | } 17 | 18 | context.formField = { 19 | ...formField, 20 | dataSource: `=${formField.id}`, 21 | }; 22 | }, 23 | true, 24 | ); 25 | } 26 | } 27 | 28 | TableDataSourceBehavior.$inject = ['eventBus']; 29 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/modeling/behavior/ValidateBehavior.js: -------------------------------------------------------------------------------- 1 | import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; 2 | 3 | export class ValidateBehavior extends CommandInterceptor { 4 | constructor(eventBus) { 5 | super(eventBus); 6 | 7 | /** 8 | * Remove custom validation if is about to be added. 9 | */ 10 | this.preExecute( 11 | 'formField.edit', 12 | function (context) { 13 | const { properties } = context; 14 | 15 | const { validate = {} } = properties; 16 | 17 | if (validate.validationType) { 18 | const newValidate = { 19 | ...validate, 20 | }; 21 | 22 | delete newValidate.minLength; 23 | delete newValidate.maxLength; 24 | delete newValidate.pattern; 25 | 26 | properties['validate'] = newValidate; 27 | } 28 | }, 29 | true, 30 | ); 31 | } 32 | } 33 | 34 | ValidateBehavior.$inject = ['eventBus']; 35 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/modeling/cmd/UpdateIdClaimHandler.js: -------------------------------------------------------------------------------- 1 | export class UpdateIdClaimHandler { 2 | /** 3 | * @constructor 4 | * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry 5 | */ 6 | constructor(formFieldRegistry) { 7 | this._formFieldRegistry = formFieldRegistry; 8 | } 9 | 10 | execute(context) { 11 | const { claiming, formField, id } = context; 12 | 13 | if (claiming) { 14 | this._formFieldRegistry._ids.claim(id, formField); 15 | } else { 16 | this._formFieldRegistry._ids.unclaim(id); 17 | } 18 | } 19 | 20 | revert(context) { 21 | const { claiming, formField, id } = context; 22 | 23 | if (claiming) { 24 | this._formFieldRegistry._ids.unclaim(id); 25 | } else { 26 | this._formFieldRegistry._ids.claim(id, formField); 27 | } 28 | } 29 | } 30 | 31 | UpdateIdClaimHandler.$inject = ['formFieldRegistry']; 32 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/modeling/index.js: -------------------------------------------------------------------------------- 1 | import commandModule from 'diagram-js/lib/command'; 2 | 3 | import { BehaviorModule } from './behavior'; 4 | import { FormLayoutUpdater } from './FormLayoutUpdater'; 5 | import { Modeling } from './Modeling'; 6 | 7 | export const ModelingModule = { 8 | __depends__: [BehaviorModule, commandModule], 9 | __init__: ['formLayoutUpdater', 'modeling'], 10 | formLayoutUpdater: ['type', FormLayoutUpdater], 11 | modeling: ['type', Modeling], 12 | }; 13 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/palette/PaletteRenderer.js: -------------------------------------------------------------------------------- 1 | import { SectionModuleBase } from '../SectionModuleBase'; 2 | 3 | export class PaletteRenderer extends SectionModuleBase { 4 | constructor(eventBus) { 5 | super(eventBus, 'palette'); 6 | } 7 | } 8 | 9 | PaletteRenderer.$inject = ['eventBus']; 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/palette/index.js: -------------------------------------------------------------------------------- 1 | import { PaletteRenderer } from './PaletteRenderer'; 2 | 3 | export const PaletteModule = { 4 | __init__: ['palette'], 5 | palette: ['type', PaletteRenderer], 6 | }; 7 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/PropertiesPanelModule.js: -------------------------------------------------------------------------------- 1 | import { SectionModuleBase } from '../SectionModuleBase'; 2 | 3 | export class PropertiesPanelModule extends SectionModuleBase { 4 | constructor(eventBus) { 5 | super(eventBus, 'propertiesPanel'); 6 | } 7 | } 8 | 9 | PropertiesPanelModule.$inject = ['eventBus']; 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/PropertiesPanelPlaceholderProvider.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provide placeholders for empty and multiple state. 3 | */ 4 | export const PropertiesPanelPlaceholderProvider = { 5 | getEmpty: () => { 6 | return { 7 | text: 'Select a form field to edit its properties.', 8 | }; 9 | }, 10 | 11 | getMultiple: () => { 12 | return { 13 | text: 'Multiple form fields are selected. Select a single form field to edit its properties.', 14 | }; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/components/index.js: -------------------------------------------------------------------------------- 1 | export { AutoFocusSelectEntry } from './AutoFocusSelect'; 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/context/FormPropertiesPanelContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | /** 4 | * @param {string} type 5 | * @param {boolean} [strict] 6 | * 7 | * @returns {any} 8 | */ 9 | function getService(type, strict) {} 10 | 11 | export const FormPropertiesPanelContext = createContext({ 12 | getService, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/context/index.js: -------------------------------------------------------------------------------- 1 | export { FormPropertiesPanelContext } from './FormPropertiesPanelContext'; 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/entries/GroupAppearanceEntry.js: -------------------------------------------------------------------------------- 1 | import { simpleBoolEntryFactory } from './factories'; 2 | 3 | export function GroupAppearanceEntry(props) { 4 | const { field } = props; 5 | 6 | const { type } = field; 7 | 8 | if (!['group', 'dynamiclist'].includes(type)) { 9 | return []; 10 | } 11 | 12 | const entries = [ 13 | simpleBoolEntryFactory({ 14 | id: 'showOutline', 15 | path: ['showOutline'], 16 | label: 'Show outline', 17 | props, 18 | }), 19 | ]; 20 | 21 | return entries; 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/entries/IFrameHeightEntry.js: -------------------------------------------------------------------------------- 1 | import { HeightEntry } from './HeightEntry'; 2 | 3 | export function IFrameHeightEntry(props) { 4 | return [ 5 | ...HeightEntry({ 6 | ...props, 7 | description: 'Height of the container in pixels.', 8 | isDefaultVisible: (field) => field.type === 'iframe', 9 | }), 10 | ]; 11 | } 12 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/entries/LayouterAppearanceEntry.js: -------------------------------------------------------------------------------- 1 | import { simpleSelectEntryFactory } from './factories'; 2 | 3 | export function LayouterAppearanceEntry(props) { 4 | const { field } = props; 5 | 6 | if (!['group', 'dynamiclist'].includes(field.type)) { 7 | return []; 8 | } 9 | 10 | const entries = [ 11 | simpleSelectEntryFactory({ 12 | id: 'verticalAlignment', 13 | path: ['verticalAlignment'], 14 | label: 'Vertical alignment', 15 | optionsArray: [ 16 | { value: 'start', label: 'Top' }, 17 | { value: 'center', label: 'Center' }, 18 | { value: 'end', label: 'Bottom' }, 19 | ], 20 | props, 21 | }), 22 | ]; 23 | 24 | return entries; 25 | } 26 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/entries/SelectEntries.js: -------------------------------------------------------------------------------- 1 | import { simpleBoolEntryFactory } from './factories'; 2 | 3 | export function SelectEntries(props) { 4 | const entries = [ 5 | simpleBoolEntryFactory({ 6 | id: 'searchable', 7 | path: ['searchable'], 8 | label: 'Searchable', 9 | props, 10 | isDefaultVisible: (field) => field.type === 'select', 11 | }), 12 | ]; 13 | 14 | return entries; 15 | } 16 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/entries/factories/index.js: -------------------------------------------------------------------------------- 1 | export { simpleStringEntryFactory } from './simpleStringEntryFactory'; 2 | export { simpleBoolEntryFactory } from './simpleBoolEntryFactory'; 3 | export { zeroPositiveIntegerEntryFactory } from './zeroPositiveIntegerEntryFactory'; 4 | export { simpleSelectEntryFactory } from './simpleSelectEntryFactory'; 5 | export { simpleRangeIntegerEntryFactory } from './simpleRangeIntegerEntryFactory'; 6 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/AppearanceGroup.js: -------------------------------------------------------------------------------- 1 | import { AdornerEntry, GroupAppearanceEntry, LayouterAppearanceEntry, MaxHeightEntry } from '../entries'; 2 | 3 | export function AppearanceGroup(field, editField, getService) { 4 | const entries = [ 5 | ...AdornerEntry({ field, editField }), 6 | ...GroupAppearanceEntry({ field, editField }), 7 | ...LayouterAppearanceEntry({ field, editField }), 8 | ...MaxHeightEntry({ field, editField }), 9 | ]; 10 | 11 | if (!entries.length) { 12 | return null; 13 | } 14 | 15 | return { 16 | id: 'appearance', 17 | label: 'Appearance', 18 | entries, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/ConditionGroup.js: -------------------------------------------------------------------------------- 1 | import { ConditionEntry } from '../entries'; 2 | 3 | export function ConditionGroup(field, editField) { 4 | const { type } = field; 5 | 6 | if (type === 'default') { 7 | return null; 8 | } 9 | 10 | const entries = [...ConditionEntry({ field, editField })]; 11 | 12 | return { 13 | id: 'condition', 14 | label: 'Condition', 15 | entries, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/ConstraintsGroup.js: -------------------------------------------------------------------------------- 1 | import { DateTimeConstraintsEntry } from '../entries'; 2 | 3 | export function ConstraintsGroup(field, editField) { 4 | const entries = [...DateTimeConstraintsEntry({ field, editField })]; 5 | 6 | if (!entries.length) { 7 | return null; 8 | } 9 | 10 | return { 11 | id: 'constraints', 12 | label: 'Constraints', 13 | entries, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/LayoutGroup.js: -------------------------------------------------------------------------------- 1 | import { ColumnsEntry } from '../entries'; 2 | 3 | export function LayoutGroup(field, editField) { 4 | const { type } = field; 5 | 6 | if (type === 'default') { 7 | return null; 8 | } 9 | 10 | const entries = [...ColumnsEntry({ field, editField })]; 11 | 12 | if (entries.length === 0) { 13 | return null; 14 | } 15 | 16 | return { 17 | id: 'layout', 18 | label: 'Layout', 19 | entries, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/SerializationGroup.js: -------------------------------------------------------------------------------- 1 | import { NumberSerializationEntry, DateTimeFormatEntry } from '../entries'; 2 | 3 | export function SerializationGroup(field, editField) { 4 | const entries = [...NumberSerializationEntry({ field, editField }), ...DateTimeFormatEntry({ field, editField })]; 5 | 6 | if (!entries.length) { 7 | return null; 8 | } 9 | 10 | return { 11 | id: 'serialization', 12 | label: 'Serialization', 13 | entries, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/groups/index.js: -------------------------------------------------------------------------------- 1 | export { GeneralGroup } from './GeneralGroup'; 2 | export { SerializationGroup } from './SerializationGroup'; 3 | export { ConstraintsGroup } from './ConstraintsGroup'; 4 | export { ValidationGroup } from './ValidationGroup'; 5 | export { OptionsGroups } from './OptionsGroups'; 6 | export { CustomPropertiesGroup } from './CustomPropertiesGroup'; 7 | export { AppearanceGroup } from './AppearanceGroup'; 8 | export { LayoutGroup } from './LayoutGroup'; 9 | export { SecurityAttributesGroup } from './SecurityAttributesGroup'; 10 | export { ConditionGroup } from './ConditionGroup'; 11 | export { TableHeaderGroups } from './TableHeaderGroups'; 12 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { useVariables } from './useVariables'; 2 | export { useService } from './usePropertiesPanelService'; 3 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/hooks/usePropertiesPanelService.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'preact/hooks'; 2 | 3 | import { FormPropertiesPanelContext } from '../context'; 4 | 5 | export function useService(type, strict) { 6 | const { getService } = useContext(FormPropertiesPanelContext); 7 | 8 | return getService(type, strict); 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/hooks/useVariables.js: -------------------------------------------------------------------------------- 1 | import { getSchemaVariables } from '@bpmn-io/form-js-viewer'; 2 | import { useService } from './usePropertiesPanelService'; 3 | 4 | /** 5 | * Retrieve list of variables from the form schema. 6 | * 7 | * @returns { string[] } list of variables used in form schema 8 | */ 9 | export function useVariables() { 10 | const form = useService('formEditor'); 11 | const schema = form.getSchema(); 12 | 13 | return getSchemaVariables(schema); 14 | } 15 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/index.js: -------------------------------------------------------------------------------- 1 | import CreateIcon from './pp-create.svg'; 2 | import ListArrowIcon from './pp-list-arrow.svg'; 3 | import ListDeleteIcon from './pp-list-delete.svg'; 4 | import SectionArrowIcon from './pp-section-arrow.svg'; 5 | 6 | export { CreateIcon, ListArrowIcon, ListDeleteIcon, SectionArrowIcon }; 7 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-checkbox-checked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-checkbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-create.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-fullpanel-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-fullpanel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-list-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-list-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/icons/pp-section-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/properties-panel/index.js: -------------------------------------------------------------------------------- 1 | import { PropertiesPanelRenderer } from './PropertiesPanelRenderer'; 2 | import { PropertiesProvider } from './PropertiesProvider'; 3 | 4 | import { FeelPopupModule } from '@bpmn-io/properties-panel'; 5 | 6 | export const PropertiesPanelModule = { 7 | __depends__: [FeelPopupModule], 8 | __init__: ['propertiesPanel', 'propertiesProvider'], 9 | propertiesPanel: ['type', PropertiesPanelRenderer], 10 | propertiesProvider: ['type', PropertiesProvider], 11 | }; 12 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/components/InjectedRendersRoot.js: -------------------------------------------------------------------------------- 1 | import { Fragment } from 'preact'; 2 | import { useMemo } from 'preact/hooks'; 3 | import { useService } from '../../../render/hooks'; 4 | import { Fill, Slot } from '../slot-fill'; 5 | 6 | /** 7 | * A functional component that holds all injected renderers. 8 | * @returns {any} The rendered component. 9 | */ 10 | export const InjectedRendersRoot = () => { 11 | const renderInjector = useService('renderInjector'); 12 | 13 | const injectedRenderers = renderInjector.fetchRenderers(); 14 | 15 | const injectedProps = useMemo( 16 | () => ({ 17 | useService, 18 | components: { 19 | Fill, 20 | Slot, 21 | }, 22 | }), 23 | [], 24 | ); 25 | 26 | return ( 27 | 28 | {injectedRenderers.map(({ Renderer }, index) => ( 29 | 30 | ))} 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/index.js: -------------------------------------------------------------------------------- 1 | import { RenderInjector } from './RenderInjector'; 2 | 3 | export const RenderInjectionModule = { 4 | __init__: ['renderInjector'], 5 | renderInjector: ['type', RenderInjector], 6 | }; 7 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/slot-fill/Fill.js: -------------------------------------------------------------------------------- 1 | import { FillContext } from './FillContext'; 2 | import { useContext, useEffect, useRef } from 'preact/compat'; 3 | 4 | export const Fill = (props) => { 5 | const uid = useRef(Symbol('fill_uid')); 6 | const fillContext = useContext(FillContext); 7 | 8 | useEffect(() => { 9 | if (!fillContext) { 10 | return; 11 | } 12 | 13 | fillContext.addFill({ id: uid, ...props }); 14 | return () => { 15 | fillContext.removeFill(uid); 16 | }; 17 | }, [fillContext, props]); 18 | 19 | return null; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/slot-fill/FillContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | export const FillContext = createContext({ 4 | addFill(uid, props) { 5 | throw new Error('FillContext.addFill() uninitialized'); 6 | }, 7 | removeFill(uid) { 8 | throw new Error('FillContext.addFill() uninitialized'); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/slot-fill/SlotContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | export const SlotContext = createContext({ fills: [] }); 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/render-injection/slot-fill/index.js: -------------------------------------------------------------------------------- 1 | export { Fill } from './Fill'; 2 | export { Slot } from './Slot'; 3 | export { SlotFillRoot } from './SlotFillRoot'; 4 | export { SlotContext } from './SlotContext'; 5 | export { FillContext } from './FillContext'; 6 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/repeat-render/index.js: -------------------------------------------------------------------------------- 1 | import { EditorRepeatRenderManager } from './EditorRepeatRenderManager'; 2 | 3 | export const RepeatRenderModule = { 4 | __init__: ['repeatRenderManager'], 5 | repeatRenderManager: ['type', EditorRepeatRenderManager], 6 | }; 7 | 8 | export { EditorRepeatRenderManager }; 9 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/selection/Selection.js: -------------------------------------------------------------------------------- 1 | export class Selection { 2 | constructor(eventBus) { 3 | this._eventBus = eventBus; 4 | this._selection = null; 5 | } 6 | 7 | get() { 8 | return this._selection; 9 | } 10 | 11 | set(selection) { 12 | if (this._selection === selection) { 13 | return; 14 | } 15 | 16 | this._selection = selection; 17 | 18 | this._eventBus.fire('selection.changed', { 19 | selection: this._selection, 20 | }); 21 | } 22 | 23 | toggle(selection) { 24 | const newSelection = this._selection === selection ? null : selection; 25 | 26 | this.set(newSelection); 27 | } 28 | 29 | clear() { 30 | this.set(null); 31 | } 32 | 33 | isSelected(formField) { 34 | return this._selection === formField; 35 | } 36 | } 37 | 38 | Selection.$inject = ['eventBus']; 39 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/features/selection/index.js: -------------------------------------------------------------------------------- 1 | import { Selection } from './Selection'; 2 | import { SelectionBehavior } from './SelectionBehavior'; 3 | 4 | export const SelectionModule = { 5 | __init__: ['selection', 'selectionBehavior'], 6 | selection: ['type', Selection], 7 | selectionBehavior: ['type', SelectionBehavior], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/index.js: -------------------------------------------------------------------------------- 1 | import { FormEditor } from './FormEditor'; 2 | 3 | import { schemaVersion } from '@bpmn-io/form-js-viewer'; 4 | 5 | export { FormEditor, schemaVersion }; 6 | 7 | export { useDebounce, usePrevious, useService } from './render/hooks'; 8 | 9 | export { useService as usePropertiesPanelService, useVariables } from './features/properties-panel/hooks'; 10 | 11 | /** 12 | * @typedef { import('./types').CreateFormEditorOptions } CreateFormEditorOptions 13 | */ 14 | 15 | /** 16 | * Create a form editor. 17 | * 18 | * @param {CreateFormEditorOptions} options 19 | * 20 | * @return {Promise} 21 | */ 22 | export function createFormEditor(options) { 23 | const { schema, ...rest } = options; 24 | 25 | const formEditor = new FormEditor(rest); 26 | 27 | return formEditor.importSchema(schema).then(() => { 28 | return formEditor; 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/EditorFormFields.js: -------------------------------------------------------------------------------- 1 | import { FormFields } from '@bpmn-io/form-js-viewer'; 2 | import { editorFormFields } from './components/editor-form-fields/'; 3 | 4 | export class EditorFormFields extends FormFields { 5 | constructor() { 6 | super(); 7 | editorFormFields.forEach((formField) => { 8 | this.register(formField.config.type, formField); 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/FieldDragPreview.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | export function FieldDragPreview(props) { 4 | const { class: className, Icon, label } = props; 5 | 6 | return ( 7 |
8 | 9 | {label} 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/editor-form-fields/EditorDocumentPreview.js: -------------------------------------------------------------------------------- 1 | import { iconsByType, DocumentPreview, Label } from '@bpmn-io/form-js-viewer'; 2 | 3 | import { editorFormFieldClasses } from '../Util'; 4 | 5 | export function EditorDocumentPreview(props) { 6 | const { field, domId } = props; 7 | 8 | const { label } = field; 9 | 10 | const Icon = iconsByType(field.type); 11 | 12 | return ( 13 |
14 |
22 | ); 23 | } 24 | 25 | EditorDocumentPreview.config = DocumentPreview.config; 26 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/editor-form-fields/EditorIFrame.js: -------------------------------------------------------------------------------- 1 | import { iconsByType, IFrame, Label } from '@bpmn-io/form-js-viewer'; 2 | 3 | import { editorFormFieldClasses } from '../Util'; 4 | 5 | export function EditorIFrame(props) { 6 | const { field, domId } = props; 7 | 8 | const { label } = field; 9 | 10 | const Icon = iconsByType(field.type); 11 | 12 | return ( 13 |
14 |
22 | ); 23 | } 24 | 25 | EditorIFrame.config = IFrame.config; 26 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/editor-form-fields/index.js: -------------------------------------------------------------------------------- 1 | import { EditorIFrame } from './EditorIFrame'; 2 | import { EditorText } from './EditorText'; 3 | import { EditorHtml } from './EditorHtml'; 4 | import { EditorTable } from './EditorTable'; 5 | import { EditorExpressionField } from './EditorExpressionField'; 6 | import { EditorDocumentPreview } from './EditorDocumentPreview'; 7 | 8 | export const editorFormFields = [ 9 | EditorIFrame, 10 | EditorText, 11 | EditorHtml, 12 | EditorTable, 13 | EditorExpressionField, 14 | EditorDocumentPreview, 15 | ]; 16 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/icons/Close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/icons/Delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/icons/Draggable.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/icons/Repeat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/components/icons/index.js: -------------------------------------------------------------------------------- 1 | import CloseIcon from './Close.svg'; 2 | import DeleteIcon from './Delete.svg'; 3 | import DraggableIcon from './Draggable.svg'; 4 | import SearchIcon from './Search.svg'; 5 | import EmptyFormIcon from './EmptyForm.svg'; 6 | 7 | export { iconsByType } from '@bpmn-io/form-js-viewer'; 8 | 9 | export { CloseIcon, DeleteIcon, DraggableIcon, SearchIcon, EmptyFormIcon }; 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/context/DragAndDropContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | export const DragAndDropContext = createContext({ 4 | drake: null, 5 | }); 6 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/context/FormEditorContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | /** 4 | * @param {string} type 5 | * @param {boolean} [strict] 6 | * 7 | * @returns {any} 8 | */ 9 | function getService(type, strict) {} 10 | 11 | export const FormEditorContext = createContext({ 12 | getService, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/context/index.js: -------------------------------------------------------------------------------- 1 | export { DragAndDropContext } from './DragAndDropContext'; 2 | export { FormEditorContext } from './FormEditorContext'; 3 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { useService } from './useService'; 2 | export { usePrevious } from './usePrevious'; 3 | export { useDebounce } from './useDebounce'; 4 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/hooks/useDebounce.js: -------------------------------------------------------------------------------- 1 | import { useMemo, useEffect } from 'preact/hooks'; 2 | 3 | import { useService } from './useService'; 4 | 5 | /** 6 | * @param {Function} fn - function to debounce 7 | */ 8 | export function useDebounce(fn) { 9 | const debounce = useService('debounce'); 10 | 11 | const callback = useMemo(() => { 12 | return debounce(fn); 13 | }, [debounce, fn]); 14 | 15 | // cleanup async side-effect if callback #flush is provided. 16 | useEffect(() => { 17 | return () => { 18 | typeof callback.flush === 'function' && callback.flush(); 19 | }; 20 | }, [callback]); 21 | 22 | return callback; 23 | } 24 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'preact/hooks'; 2 | 3 | export function usePrevious(value, defaultValue = null) { 4 | const ref = useRef(defaultValue); 5 | 6 | useEffect(() => (ref.current = value), [value]); 7 | 8 | return ref.current; 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/hooks/useService.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'preact/hooks'; 2 | 3 | import { FormEditorContext } from '../context'; 4 | 5 | export function useService(type, strict) { 6 | const { getService } = useContext(FormEditorContext); 7 | 8 | return getService(type, strict); 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/index.js: -------------------------------------------------------------------------------- 1 | import { EditorFormFields } from './EditorFormFields'; 2 | import { Renderer } from './Renderer'; 3 | 4 | export const RenderModule = { 5 | __init__: ['formFields', 'renderer'], 6 | formFields: ['type', EditorFormFields], 7 | renderer: ['type', Renderer], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/render/util/Cursor.js: -------------------------------------------------------------------------------- 1 | import { classes as domClasses } from 'min-dom'; 2 | 3 | const CURSOR_CLS_PATTERN = /^fjs-cursor-.*$/; 4 | 5 | export function set(mode) { 6 | const classes = domClasses(document.body); 7 | 8 | classes.removeMatching(CURSOR_CLS_PATTERN); 9 | 10 | if (mode) { 11 | classes.add('fjs-cursor-' + mode); 12 | } 13 | } 14 | 15 | export function unset() { 16 | set(null); 17 | } 18 | 19 | export function has(mode) { 20 | const classes = domClasses(document.body); 21 | 22 | return classes.has('fjs-cursor-' + mode); 23 | } 24 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/types.d.ts: -------------------------------------------------------------------------------- 1 | import { Injector } from 'didi'; 2 | 3 | export type Module = any; 4 | export type Schema = any; 5 | 6 | export interface FormEditorProperties { 7 | [x: string]: any; 8 | } 9 | 10 | export interface FormEditorOptions { 11 | additionalModules?: Module[]; 12 | container?: Element | null | string; 13 | exporter?: { 14 | name: string; 15 | version: string; 16 | }; 17 | injector?: Injector; 18 | modules?: Module[]; 19 | properties?: FormEditorProperties; 20 | [x: string]: any; 21 | } 22 | 23 | export interface CreateFormEditorOptions extends FormEditorOptions { 24 | schema?: Schema; 25 | } 26 | 27 | export { Injector }; 28 | -------------------------------------------------------------------------------- /packages/form-js-editor/src/types/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const content: any; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/TestHelper.js: -------------------------------------------------------------------------------- 1 | import 'preact/debug'; 2 | 3 | import { insertCSS } from './helper'; 4 | 5 | // @ts-ignore-next-line 6 | import formCSS from '@bpmn-io/form-js-viewer/dist/assets/form-js.css'; 7 | 8 | // @ts-ignore-next-line 9 | import formEditorCSS from '../dist/assets/form-js-editor.css'; 10 | 11 | // @ts-ignore-next-line 12 | import testCSS from './test.css'; 13 | 14 | export { expectNoViolations } from '../../form-js-viewer/test/helper'; 15 | 16 | export { insertTheme } from '../../form-js-viewer/test/TestHelper'; 17 | 18 | export function isSingleStart(topic) { 19 | // @ts-ignore-next-line 20 | return window.__env__ && window.__env__.SINGLE_START === topic; 21 | } 22 | 23 | export function insertStyles() { 24 | insertCSS('form-js.css', formCSS); 25 | insertCSS('form-js-editor.css', formEditorCSS); 26 | insertCSS('test-editor.css', testCSS); 27 | } 28 | 29 | insertStyles(); 30 | 31 | export { createFormContainer } from '../../form-js-viewer/test/TestHelper'; 32 | 33 | export * from './helper'; 34 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/coverageBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | 5 | const allSources = require.context('../src', true, /.*\.js$/); 6 | 7 | allSources.keys().forEach(allSources); 8 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/spec/features/properties-panel/helper/mocks/index.js: -------------------------------------------------------------------------------- 1 | export * from '../../../../../helper/mocks'; 2 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/spec/form-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "id": "Textfield_1", 5 | "key": "invoiceNumber", 6 | "label": "Invoice Number", 7 | "type": "textfield", 8 | "layout": { 9 | "row": "Row_1", 10 | "columns": 16 11 | } 12 | }, 13 | { 14 | "components": [ 15 | { 16 | "label": "Text field", 17 | "type": "textfield", 18 | "layout": { 19 | "row": "Row_3", 20 | "columns": null 21 | }, 22 | "id": "Textfield_2", 23 | "key": "textfield_2" 24 | } 25 | ], 26 | "showOutline": true, 27 | "label": "Group", 28 | "type": "group", 29 | "layout": { 30 | "row": "Row_2", 31 | "columns": null 32 | }, 33 | "id": "Group_1" 34 | } 35 | ], 36 | "type": "default", 37 | "id": "Form_1", 38 | "schemaVersion": 11 39 | } 40 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/spec/form-table.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "id": "Form_1", 4 | "type": "default", 5 | "components": [ 6 | { 7 | "label": "static-headers-table", 8 | "type": "table", 9 | "rowCount": 10, 10 | "columns": [ 11 | { 12 | "label": "ID", 13 | "key": "id" 14 | }, 15 | { 16 | "label": "Name", 17 | "key": "name" 18 | }, 19 | { 20 | "label": "Date", 21 | "key": "date" 22 | } 23 | ], 24 | "id": "Field_0k6resc1", 25 | "dataSource": "Field_0k6resc1" 26 | }, 27 | { 28 | "label": "dynamic-headers-table", 29 | "type": "table", 30 | "rowCount": 10, 31 | "columnsExpression": "=tableHeaders", 32 | "id": "Field_0k6resc2", 33 | "dataSource": "Field_0k6resc2" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/spec/other.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "text", 6 | "text": "# File an Invoice\n\nAdd your invoice details below." 7 | }, 8 | { 9 | "key": "creditor", 10 | "label": "Creditor", 11 | "type": "textfield", 12 | "validate": { 13 | "required": true 14 | } 15 | }, 16 | { 17 | "description": "An invoice number in the format: C-123.", 18 | "key": "invoiceNumber", 19 | "label": "Invoice Number", 20 | "type": "textfield", 21 | "validate": { 22 | "pattern": "^C-[0-9]+$" 23 | } 24 | }, 25 | { 26 | "label": "Submit", 27 | "type": "button" 28 | } 29 | ], 30 | "type": "default" 31 | } 32 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/spec/redundantValues.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "redundantValues", 6 | "label": "Redundant Values", 7 | "type": "radio", 8 | "values": [ 9 | { 10 | "label": "Value 2", 11 | "value": "value2" 12 | }, 13 | { 14 | "label": "Value 3", 15 | "value": "value3" 16 | } 17 | ], 18 | "properties": { 19 | "key2": "value" 20 | } 21 | } 22 | ], 23 | "type": "default" 24 | } 25 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/test.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap'); 2 | 3 | html, 4 | body, 5 | .fjs-editor { 6 | height: 100%; 7 | } 8 | 9 | body { 10 | --font-family: 'IBM Plex Sans', sans-serif; 11 | font-size: 16px; 12 | margin: 0; 13 | } 14 | 15 | .test-container { 16 | height: auto !important; 17 | } 18 | 19 | .bio-properties-panel { 20 | --font-family: inherit; 21 | --font-family-monospace: inherit; 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-editor/test/testBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | -------------------------------------------------------------------------------- /packages/form-js-integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bpmn-io/form-js-integration", 3 | "version": "1.15.2", 4 | "description": "A set of form-js integration testing resources", 5 | "private": true, 6 | "scripts": { 7 | "test": "run-s test:types", 8 | "test:types": "tsc src/application.ts --outDir dist" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/bpmn-io/form-js.git", 13 | "directory": "packages/form-js-integration" 14 | }, 15 | "author": { 16 | "name": "bpmn.io contributors", 17 | "url": "https://github.com/bpmn-io" 18 | }, 19 | "dependencies": { 20 | "@bpmn-io/form-js": "^1.15.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-playground/resources/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/packages/form-js-playground/resources/screenshot.png -------------------------------------------------------------------------------- /packages/form-js-playground/src/components/FileDrop.css: -------------------------------------------------------------------------------- 1 | /** 2 | * file-drop.css 3 | */ 4 | .drop-overlay { 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | bottom: 0; 9 | right: 0; 10 | 11 | padding: 50px; 12 | 13 | background: rgba(255, 255, 255, 0.9); 14 | 15 | z-index: 1000; 16 | } 17 | 18 | .drop-overlay .box { 19 | text-align: center; 20 | border: dashed 4px #ccc; 21 | height: 100%; 22 | width: 100%; 23 | display: table; 24 | } 25 | 26 | .drop-overlay .label { 27 | font-size: 26px; 28 | color: #888; 29 | margin: auto; 30 | 31 | display: table-cell; 32 | vertical-align: middle; 33 | } 34 | -------------------------------------------------------------------------------- /packages/form-js-playground/src/components/Section.js: -------------------------------------------------------------------------------- 1 | export function Section(props) { 2 | const elements = Array.isArray(props.children) ? props.children : [props.children]; 3 | 4 | const { headerItems, children } = elements.reduce( 5 | (_, child) => { 6 | const bucket = child.type === Section.HeaderItem ? _.headerItems : _.children; 7 | 8 | bucket.push(child); 9 | 10 | return _; 11 | }, 12 | { headerItems: [], children: [] }, 13 | ); 14 | 15 | return ( 16 |
17 |

18 | {props.name} {headerItems.length ? {headerItems} : null} 19 |

20 |
{children}
21 |
22 | ); 23 | } 24 | 25 | Section.HeaderItem = function (props) { 26 | return props.children; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/form-js-playground/src/components/autocompletion/VariablesFacet.js: -------------------------------------------------------------------------------- 1 | import { Facet } from '@codemirror/state'; 2 | 3 | /** 4 | * @type {Facet} Variables 5 | */ 6 | export const variablesFacet = Facet.define(); 7 | -------------------------------------------------------------------------------- /packages/form-js-playground/src/index.js: -------------------------------------------------------------------------------- 1 | export { Playground } from './Playground'; 2 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/TestHelper.js: -------------------------------------------------------------------------------- 1 | import './test.css'; 2 | 3 | import '@bpmn-io/form-js-viewer/dist/assets/form-js.css'; 4 | 5 | import '@bpmn-io/form-js-editor/dist/assets/form-js-editor.css'; 6 | 7 | export { expectNoViolations } from '../../form-js-viewer/test/helper'; 8 | 9 | export function isSingleStart(topic) { 10 | return window.__env__ && window.__env__.SINGLE_START === topic; 11 | } 12 | 13 | export function insertCSS(name, css) { 14 | if (document.querySelector('[data-css-file="' + name + '"]')) { 15 | return; 16 | } 17 | 18 | const head = document.head || document.getElementsByTagName('head')[0]; 19 | const style = document.createElement('style'); 20 | style.setAttribute('data-css-file', name); 21 | 22 | style.type = 'text/css'; 23 | style.appendChild(document.createTextNode(css)); 24 | 25 | head.appendChild(style); 26 | } 27 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/coverageBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | 5 | const allSources = require.context('../src', true, /.*\.js$/); 6 | 7 | allSources.keys().forEach(allSources); 8 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/custom/styles.css: -------------------------------------------------------------------------------- 1 | .range-group { 2 | display: flex; 3 | flex-direction: row; 4 | } 5 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/spec/custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "key": "creditor", 5 | "label": "Creditor", 6 | "type": "textfield", 7 | "validate": { 8 | "required": true 9 | } 10 | }, 11 | { 12 | "key": "amount", 13 | "type": "range", 14 | "label": "Amount", 15 | "range": { 16 | "min": 0, 17 | "max": 100, 18 | "step": 5 19 | } 20 | }, 21 | { 22 | "type": "button", 23 | "action": "submit", 24 | "label": "Submit" 25 | } 26 | ], 27 | "type": "default" 28 | } 29 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/spec/other-form.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "action": "reset", 6 | "id": "foo", 7 | "label": "Reset", 8 | "type": "button" 9 | } 10 | ], 11 | "type": "default" 12 | } 13 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/test.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | font-size: 16px; 4 | margin: 1rem; 5 | } 6 | 7 | h1 { 8 | margin: 0 0 1rem 0; 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-playground/test/testBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/assets/grid.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/grid'; 2 | 3 | // Emit the flex-grid styles 4 | @include grid.flex-grid(); 5 | -------------------------------------------------------------------------------- /packages/form-js-viewer/assets/index.scss: -------------------------------------------------------------------------------- 1 | @use 'form-js-base.css'; 2 | @use 'grid.scss'; 3 | @use 'flatpickr/dist/themes/light.css'; 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/core/EventBus.js: -------------------------------------------------------------------------------- 1 | export { default as EventBus } from 'diagram-js/lib/core/EventBus'; 2 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/core/index.js: -------------------------------------------------------------------------------- 1 | import { EventBus } from './EventBus'; 2 | import { Validator } from './Validator'; 3 | import { Importer } from './Importer'; 4 | import { FieldFactory } from './FieldFactory'; 5 | import { PathRegistry } from './PathRegistry'; 6 | import { FormLayouter } from './FormLayouter'; 7 | import { FormFieldRegistry } from './FormFieldRegistry'; 8 | import { FormFieldInstanceRegistry } from './FormFieldInstanceRegistry'; 9 | 10 | import { RenderModule } from '../render'; 11 | 12 | export { Importer, FieldFactory, FormFieldRegistry, PathRegistry, FormLayouter }; 13 | 14 | export const CoreModule = { 15 | __depends__: [RenderModule], 16 | eventBus: ['type', EventBus], 17 | importer: ['type', Importer], 18 | fieldFactory: ['type', FieldFactory], 19 | formFieldRegistry: ['type', FormFieldRegistry], 20 | formFieldInstanceRegistry: ['type', FormFieldInstanceRegistry], 21 | pathRegistry: ['type', PathRegistry], 22 | formLayouter: ['type', FormLayouter], 23 | validator: ['type', Validator], 24 | }; 25 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/expressionField/index.js: -------------------------------------------------------------------------------- 1 | import { ExpressionLoopPreventer } from './ExpressionLoopPreventer'; 2 | 3 | export const ExpressionFieldModule = { 4 | __init__: ['expressionLoopPreventer'], 5 | expressionLoopPreventer: ['type', ExpressionLoopPreventer], 6 | }; 7 | 8 | export { ExpressionLoopPreventer }; 9 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/expressionLanguage/index.js: -------------------------------------------------------------------------------- 1 | import { FeelExpressionLanguage } from './FeelExpressionLanguage'; 2 | import { FeelersTemplating } from './FeelersTemplating'; 3 | import { ConditionChecker } from './ConditionChecker'; 4 | 5 | export const ExpressionLanguageModule = { 6 | __init__: ['expressionLanguage', 'templating', 'conditionChecker'], 7 | expressionLanguage: ['type', FeelExpressionLanguage], 8 | templating: ['type', FeelersTemplating], 9 | conditionChecker: ['type', ConditionChecker], 10 | }; 11 | 12 | export { FeelExpressionLanguage, FeelersTemplating, ConditionChecker }; 13 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/index.js: -------------------------------------------------------------------------------- 1 | export { ExpressionLanguageModule } from './expressionLanguage'; 2 | export { ExpressionFieldModule } from './expressionField'; 3 | export { MarkdownRendererModule } from './markdown'; 4 | export { ViewerCommandsModule } from './viewerCommands'; 5 | export { RepeatRenderModule } from './repeatRender'; 6 | 7 | export * from './expressionLanguage'; 8 | export * from './expressionField'; 9 | export * from './markdown'; 10 | export * from './viewerCommands'; 11 | export * from './repeatRender'; 12 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/markdown/MarkdownRenderer.js: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked'; 2 | 3 | export class MarkdownRenderer { 4 | /** 5 | * Render markdown to HTML. 6 | * 7 | * @param {string} markdown - The markdown to render 8 | * 9 | * @returns {string} HTML 10 | */ 11 | render(markdown) { 12 | // @ts-expect-error 13 | return marked.parse(markdown, { 14 | gfm: true, 15 | breaks: true, 16 | }); 17 | } 18 | } 19 | 20 | MarkdownRenderer.$inject = []; 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/markdown/index.js: -------------------------------------------------------------------------------- 1 | import { MarkdownRenderer } from './MarkdownRenderer'; 2 | 3 | export const MarkdownRendererModule = { 4 | __init__: ['markdownRenderer'], 5 | markdownRenderer: ['type', MarkdownRenderer], 6 | }; 7 | 8 | export { MarkdownRenderer }; 9 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/repeatRender/index.js: -------------------------------------------------------------------------------- 1 | import { RepeatRenderManager } from './RepeatRenderManager'; 2 | 3 | export const RepeatRenderModule = { 4 | __init__: ['repeatRenderManager'], 5 | repeatRenderManager: ['type', RepeatRenderManager], 6 | }; 7 | 8 | export { RepeatRenderManager }; 9 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/viewerCommands/cmd/UpdateFieldValidationHandler.js: -------------------------------------------------------------------------------- 1 | import { set } from 'min-dash'; 2 | import { clone } from '../../../util'; 3 | 4 | /** 5 | * @deprecated 6 | */ 7 | export class UpdateFieldValidationHandler { 8 | constructor(form, validator) { 9 | this._form = form; 10 | this._validator = validator; 11 | } 12 | 13 | execute(context) { 14 | const { field, value, indexes } = context; 15 | const { errors } = this._form._getState(); 16 | 17 | context.oldErrors = clone(errors); 18 | 19 | const fieldErrors = this._validator.validateField(field, value); 20 | const updatedErrors = set( 21 | errors, 22 | [field.id, ...Object.values(indexes || {})], 23 | fieldErrors.length ? fieldErrors : undefined, 24 | ); 25 | this._form._setState({ errors: updatedErrors }); 26 | } 27 | 28 | revert(context) { 29 | this._form._setState({ errors: context.oldErrors }); 30 | } 31 | } 32 | 33 | UpdateFieldValidationHandler.$inject = ['form', 'validator']; 34 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/features/viewerCommands/index.js: -------------------------------------------------------------------------------- 1 | import commandModule from 'diagram-js/lib/command'; 2 | 3 | import { ViewerCommands } from './ViewerCommands'; 4 | 5 | export const ViewerCommandsModule = { 6 | __depends__: [commandModule], 7 | __init__: ['viewerCommands'], 8 | viewerCommands: ['type', ViewerCommands], 9 | }; 10 | 11 | export { ViewerCommands }; 12 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/index.js: -------------------------------------------------------------------------------- 1 | import { Form } from './Form'; 2 | 3 | export { FormFieldRegistry, FormLayouter, Importer, FieldFactory, PathRegistry } from './core'; 4 | export * from './render'; 5 | export * from './util'; 6 | export * from './features'; 7 | 8 | const schemaVersion = 18; 9 | 10 | export { Form, schemaVersion }; 11 | 12 | /** 13 | * @typedef { import('./types').CreateFormOptions } CreateFormOptions 14 | */ 15 | 16 | /** 17 | * Create a form. 18 | * 19 | * @param {CreateFormOptions} options 20 | * 21 | * @return {Promise
} 22 | */ 23 | export function createForm(options) { 24 | const { data, schema, ...formOptions } = options; 25 | 26 | const form = new Form(formOptions); 27 | 28 | return form.importSchema(schema, data).then(function () { 29 | return form; 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/FormFields.js: -------------------------------------------------------------------------------- 1 | import { formFields } from './components'; 2 | 3 | export class FormFields { 4 | constructor() { 5 | this._formFields = {}; 6 | 7 | formFields.forEach((formField) => { 8 | this.register(formField.config.type, formField); 9 | }); 10 | } 11 | 12 | register(type, formField) { 13 | this._formFields[type] = formField; 14 | } 15 | 16 | get(type) { 17 | return this._formFields[type]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bpmn-io/form-js/7e3fcd5ea1a8937bc7a7c91972cec45600728105/packages/form-js-viewer/src/render/components/.DS_Store -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/Description.js: -------------------------------------------------------------------------------- 1 | import { useSingleLineTemplateEvaluation } from '../hooks'; 2 | 3 | export function Description(props) { 4 | const { description, id } = props; 5 | 6 | const evaluatedDescription = useSingleLineTemplateEvaluation(description || '', { debug: true }); 7 | 8 | if (!evaluatedDescription) { 9 | return null; 10 | } 11 | 12 | return ( 13 |
14 | {evaluatedDescription} 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/Errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef Props 3 | * @property {string} id 4 | * @property {string[]} errors 5 | * 6 | * @param {Props} props 7 | * @returns {import("preact").JSX.Element} 8 | */ 9 | export function Errors(props) { 10 | const { errors, id } = props; 11 | 12 | if (!errors.length) { 13 | return null; 14 | } 15 | 16 | return ( 17 |
18 |
    19 | {errors.map((error, index) => { 20 | return
  • {error}
  • ; 21 | })} 22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/Button.js: -------------------------------------------------------------------------------- 1 | import { formFieldClasses } from '../Util'; 2 | import { useSingleLineTemplateEvaluation } from '../../hooks'; 3 | 4 | const type = 'button'; 5 | 6 | export function Button(props) { 7 | const { disabled, onFocus, onBlur, field } = props; 8 | 9 | const { action = 'submit' } = field; 10 | 11 | const evaluatedLabel = useSingleLineTemplateEvaluation(field.label || '', { debug: true }); 12 | 13 | return ( 14 |
15 | 23 |
24 | ); 25 | } 26 | 27 | Button.config = { 28 | type, 29 | keyed: false, 30 | name: 'Button', 31 | group: 'action', 32 | create: (options = {}) => ({ 33 | label: 'Button', 34 | action: 'submit', 35 | ...options, 36 | }), 37 | }; 38 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/Default.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'preact/hooks'; 2 | import { FormRenderContext } from '../../context'; 3 | import { ChildrenRenderer } from './parts/ChildrenRenderer'; 4 | 5 | export function Default(props) { 6 | const { Empty } = useContext(FormRenderContext); 7 | 8 | const fullProps = { ...props, Empty }; 9 | 10 | return ; 11 | } 12 | 13 | Default.config = { 14 | type: 'default', 15 | keyed: false, 16 | label: null, 17 | group: null, 18 | create: (options = {}) => ({ 19 | components: [], 20 | ...options, 21 | }), 22 | getSubheading: (field) => field.id, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/Separator.js: -------------------------------------------------------------------------------- 1 | import { formFieldClasses } from '../Util'; 2 | 3 | const type = 'separator'; 4 | 5 | export function Separator() { 6 | return ( 7 |
8 |
9 |
10 | ); 11 | } 12 | 13 | Separator.config = { 14 | type, 15 | keyed: false, 16 | name: 'Separator', 17 | group: 'presentation', 18 | create: (options = {}) => ({ 19 | ...options, 20 | }), 21 | }; 22 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/Spacer.js: -------------------------------------------------------------------------------- 1 | import { formFieldClasses } from '../Util'; 2 | 3 | const type = 'spacer'; 4 | 5 | export function Spacer(props) { 6 | const { field } = props; 7 | const { height = 60 } = field; 8 | 9 | return
; 10 | } 11 | 12 | Spacer.config = { 13 | type, 14 | keyed: false, 15 | name: 'Spacer', 16 | group: 'presentation', 17 | create: (options = {}) => ({ 18 | height: 60, 19 | ...options, 20 | }), 21 | }; 22 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Add.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/AngelDown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/AngelUp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/ArrowDown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/ArrowUp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/CaretLeft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/CaretRight.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Clock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Collapse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/Expand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/ImagePlaceholder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/icons/XMark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/parts/SkipLink.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | 3 | import { useCallback } from 'preact/hooks'; 4 | 5 | export function SkipLink(props) { 6 | const { className, label, onSkip } = props; 7 | 8 | const onKeyDown = useCallback( 9 | (event) => { 10 | if (event.key === 'Enter') { 11 | event.preventDefault(); 12 | event.stopPropagation(); 13 | onSkip(); 14 | } 15 | }, 16 | [onSkip], 17 | ); 18 | 19 | return ( 20 | 21 | {label} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/form-fields/parts/TemplatedInputAdorner.js: -------------------------------------------------------------------------------- 1 | import { InputAdorner } from './InputAdorner'; 2 | 3 | import { useSingleLineTemplateEvaluation } from '../../../hooks'; 4 | 5 | export function TemplatedInputAdorner(props) { 6 | const { pre, post } = props; 7 | 8 | const evaluatedPre = useSingleLineTemplateEvaluation(pre, { debug: true }); 9 | const evaluatedPost = useSingleLineTemplateEvaluation(post, { debug: true }); 10 | 11 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Button.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Checkbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Datetime.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/DocumentPreview.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/FilePicker.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Radio.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Select.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Separator.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Spacer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Table.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Taglist.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Textarea.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/components/icons/Textfield.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/context/FormContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | /** 4 | * @param {string} type 5 | * @param {boolean} [strict] 6 | * 7 | * @returns {any} 8 | */ 9 | function getService(type, strict) {} 10 | 11 | export const FormContext = createContext({ 12 | getService, 13 | formId: null, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/context/LocalExpressionContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'preact'; 2 | 3 | export const LocalExpressionContext = createContext({ 4 | data: null, 5 | this: null, 6 | parent: null, 7 | i: null, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/context/index.js: -------------------------------------------------------------------------------- 1 | export { FormRenderContext } from './FormRenderContext'; 2 | export { LocalExpressionContext } from './LocalExpressionContext'; 3 | export { FormContext } from './FormContext'; 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useCleanupSingleSelectValue.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'preact/hooks'; 2 | import { LOAD_STATES } from './useOptionsAsync'; 3 | import { hasEqualValue } from '../components/util/sanitizerUtil'; 4 | 5 | export function useCleanupSingleSelectValue(props) { 6 | const { field, options, loadState, onChange, value } = props; 7 | 8 | // Ensures that the value is always one of the possible options 9 | useEffect(() => { 10 | if (loadState !== LOAD_STATES.LOADED) { 11 | return; 12 | } 13 | 14 | const optionValues = options.map((o) => o.value); 15 | const hasValueNotInOptions = value && !hasEqualValue(value, optionValues); 16 | 17 | if (hasValueNotInOptions) { 18 | onChange({ 19 | field, 20 | value: null, 21 | }); 22 | } 23 | }, [field, options, onChange, value, loadState]); 24 | } 25 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useCondition.js: -------------------------------------------------------------------------------- 1 | import { useService } from './useService.js'; 2 | import { useContext, useMemo } from 'preact/hooks'; 3 | import { LocalExpressionContext } from '../context/LocalExpressionContext.js'; 4 | import { buildExpressionContext } from '../../util/expressions.js'; 5 | 6 | /** 7 | * Evaluate if condition is met reactively based on the conditionChecker and form data. 8 | * 9 | * @param {string | undefined} condition 10 | * 11 | * @returns {boolean} true if condition is met or no condition or condition checker exists 12 | */ 13 | export function useCondition(condition) { 14 | const conditionChecker = useService('conditionChecker', false); 15 | const expressionContextInfo = useContext(LocalExpressionContext); 16 | 17 | return useMemo(() => { 18 | return conditionChecker ? conditionChecker.check(condition, buildExpressionContext(expressionContextInfo)) : null; 19 | }, [conditionChecker, condition, expressionContextInfo]); 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useDeepCompareMemoize.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'preact/hooks'; 2 | import isEqual from 'lodash/isEqual'; 3 | 4 | /** 5 | * A custom hook to manage state changes with deep comparison. 6 | * 7 | * @template T 8 | * @param {T} value - The current value to manage. 9 | * @returns {T} - Returns the current state. 10 | */ 11 | export function useDeepCompareMemoize(value) { 12 | /** @type {import("preact").RefObject} */ 13 | const ref = useRef(); 14 | 15 | if (!isEqual(value, ref.current)) { 16 | ref.current = value; 17 | } 18 | 19 | return ref.current; 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useExpressionEvaluation.js: -------------------------------------------------------------------------------- 1 | import { useService } from './useService'; 2 | import { LocalExpressionContext } from '../context/LocalExpressionContext'; 3 | import { useContext, useMemo } from 'preact/hooks'; 4 | import { runExpressionEvaluation } from '../../util/expressions'; 5 | 6 | /** 7 | * If the value is a valid expression, it is evaluated and returned. Otherwise, it is returned as-is. 8 | * The function is memoized to minimize re-renders. 9 | * 10 | * @param {any} value - A static value or expression to evaluate. 11 | * @returns {any} - Evaluated value or the original value if not an expression. 12 | */ 13 | export function useExpressionEvaluation(value) { 14 | const expressionLanguage = useService('expressionLanguage'); 15 | const expressionContextInfo = useContext(LocalExpressionContext); 16 | return useMemo( 17 | () => runExpressionEvaluation(expressionLanguage, value, expressionContextInfo), 18 | [expressionLanguage, expressionContextInfo, value], 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useFilteredFormData.js: -------------------------------------------------------------------------------- 1 | import { useService } from './useService.js'; 2 | import { useMemo } from 'preact/hooks'; 3 | 4 | /** 5 | * Returns the conditionally filtered data of a form reactively. 6 | * Memoised to minimize re-renders 7 | * 8 | * Warning: costly operation, use with care 9 | */ 10 | export function useFilteredFormData() { 11 | const { initialData, data } = useService('form')._getState(); 12 | const conditionChecker = useService('conditionChecker', false); 13 | 14 | return useMemo(() => { 15 | const newData = conditionChecker ? conditionChecker.applyConditions(data, data) : data; 16 | return { ...initialData, ...newData }; 17 | }, [conditionChecker, data, initialData]); 18 | } 19 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useGetLabelCorrelation.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo } from 'preact/hooks'; 2 | import { isObject } from 'min-dash'; 3 | 4 | /** 5 | * This hook allows us to retrieve the label from a value in linear time by caching it in a map 6 | * @param {Array} options 7 | */ 8 | export function useGetLabelCorrelation(options) { 9 | // This allows us to retrieve the label from a value in linear time 10 | const labelMap = useMemo( 11 | () => Object.assign({}, ...options.map((o) => ({ [_getValueHash(o.value)]: o.label }))), 12 | [options], 13 | ); 14 | return useCallback((value) => labelMap[_getValueHash(value)], [labelMap]); 15 | } 16 | 17 | const _getValueHash = (value) => { 18 | return isObject(value) ? JSON.stringify(value) : value; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useKeyDownAction.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'preact/hooks'; 2 | 3 | export function useKeyDownAction(targetKey, action, listenerElement = window) { 4 | function downHandler({ key }) { 5 | if (key === targetKey) { 6 | action(); 7 | } 8 | } 9 | 10 | useEffect(() => { 11 | listenerElement.addEventListener('keydown', downHandler); 12 | 13 | return () => { 14 | listenerElement.removeEventListener('keydown', downHandler); 15 | }; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'preact/hooks'; 2 | 3 | export function usePrevious(value, defaultValue = null) { 4 | const ref = useRef(defaultValue); 5 | 6 | useEffect(() => (ref.current = value), [value]); 7 | 8 | return ref.current; 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useService.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'preact/hooks'; 2 | 3 | import { FormContext } from '../context'; 4 | 5 | /** 6 | * @template T 7 | * @param {string} type 8 | * @param {boolean} [strict=true] 9 | * @returns {T | null} 10 | */ 11 | export function useService(type, strict) { 12 | const { getService } = useContext(FormContext); 13 | 14 | return getService(type, strict); 15 | } 16 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/hooks/useSingleLineTemplateEvaluation.js: -------------------------------------------------------------------------------- 1 | import { useTemplateEvaluation } from './useTemplateEvaluation'; 2 | import { useMemo } from 'preact/hooks'; 3 | 4 | /** 5 | * Template a string reactively based on form data. If the string is not a template, it is returned as is. 6 | * If the string contains multiple lines, only the first line is returned. 7 | * Memoised to minimize re-renders 8 | * 9 | * @param {string} value 10 | * @param {Object} [options] 11 | * @param {boolean} [options.debug = false] 12 | * @param {boolean} [options.strict = false] 13 | * @param {Function} [options.buildDebugString] 14 | * 15 | */ 16 | export function useSingleLineTemplateEvaluation(value, options = {}) { 17 | const evaluatedTemplate = useTemplateEvaluation(value, options); 18 | return useMemo(() => evaluatedTemplate && evaluatedTemplate.split('\n')[0], [evaluatedTemplate]); 19 | } 20 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/render/index.js: -------------------------------------------------------------------------------- 1 | import { FormFields } from './FormFields'; 2 | import { Renderer } from './Renderer'; 3 | import { FileRegistry } from './FileRegistry'; 4 | 5 | export { FormFields }; 6 | 7 | export * from './components'; 8 | export * from './context'; 9 | export { useExpressionEvaluation, useSingleLineTemplateEvaluation, useTemplateEvaluation } from './hooks'; 10 | 11 | export const RenderModule = { 12 | __init__: ['formFields', 'renderer'], 13 | formFields: ['type', FormFields], 14 | renderer: ['type', Renderer], 15 | fileRegistry: ['type', FileRegistry], 16 | }; 17 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/types.d.ts: -------------------------------------------------------------------------------- 1 | import { Injector } from 'didi'; 2 | 3 | export type Module = any; 4 | export type Schema = any; 5 | 6 | export interface Data { 7 | [x: string]: any; 8 | } 9 | 10 | export interface Errors { 11 | [x: string]: string[]; 12 | } 13 | 14 | export type FormProperty = 'readOnly' | 'disabled' | string; 15 | export type FormEvent = 'submit' | 'changed' | string; 16 | 17 | export interface FormProperties { 18 | [x: string]: any; 19 | } 20 | 21 | export interface FormOptions { 22 | additionalModules?: Module[]; 23 | container?: Element | null | string; 24 | injector?: Injector; 25 | modules?: Module[]; 26 | properties?: FormProperties; 27 | } 28 | 29 | export interface CreateFormOptions extends FormOptions { 30 | data?: Data; 31 | schema: Schema; 32 | } 33 | 34 | export { Injector }; 35 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/constants/FilePickerConstants.js: -------------------------------------------------------------------------------- 1 | const FILE_PICKER_FILE_KEY_PREFIX = 'files::'; 2 | 3 | export { FILE_PICKER_FILE_KEY_PREFIX }; 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/constants/index.js: -------------------------------------------------------------------------------- 1 | export * from './DatetimeConstants'; 2 | export * from './OptionsSourceConstants'; 3 | export * from './IFrameConstants'; 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string?} prefix 3 | * 4 | * @returns Element 5 | */ 6 | export function createFormContainer(prefix = 'fjs') { 7 | const container = document.createElement('div'); 8 | 9 | container.classList.add(`${prefix}-container`); 10 | 11 | return container; 12 | } 13 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/index.js: -------------------------------------------------------------------------------- 1 | export * from './constants'; 2 | export * from './injector'; 3 | export * from './form'; 4 | export * from './getSchemaVariables'; 5 | export * from './simple'; 6 | export * from './structure'; 7 | export * from './expressions'; 8 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/injector.js: -------------------------------------------------------------------------------- 1 | import { Injector } from 'didi'; 2 | 3 | export function createInjector(bootstrapModules) { 4 | const injector = new Injector(bootstrapModules); 5 | 6 | injector.init(); 7 | 8 | return injector; 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js-viewer/src/util/structure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the ancestry list of a form field. 3 | * 4 | * @param {string} formFieldId 5 | * @param {import('../core/FormFieldRegistry').FormFieldRegistry} formFieldRegistry 6 | * 7 | * @return {Array} ancestry list 8 | */ 9 | export const getAncestryList = (formFieldId, formFieldRegistry) => { 10 | const ids = []; 11 | 12 | let currentFormField = formFieldRegistry.get(formFieldId); 13 | 14 | while (currentFormField) { 15 | ids.push(currentFormField.id); 16 | 17 | currentFormField = formFieldRegistry.get(currentFormField._parent); 18 | } 19 | 20 | return ids; 21 | }; 22 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/coverageBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | 5 | const allSources = require.context('../src', true, /.*\.js$/); 6 | 7 | allSources.keys().forEach(allSources); 8 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "textfield", 6 | "key": "adorner_expression", 7 | "appearance": { 8 | "prefixAdorner": "=prefix_expression", 9 | "suffixAdorner": "=suffix_expression" 10 | } 11 | }, 12 | { 13 | "type": "textfield", 14 | "key": "adorner_template", 15 | "appearance": { 16 | "prefixAdorner": "{{ prefix_template }}", 17 | "suffixAdorner": "{{ suffix_template }}" 18 | } 19 | } 20 | ], 21 | "type": "default" 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/complex-conditions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "label": "Complex display condition 1", 6 | "type": "textarea", 7 | "key": "c1", 8 | "conditional": { 9 | "hide": "={\nremainingAllowance: loanOptions.maxAllowance - loanOptions.currentlyUsed,\nrestAfterNewLoan: remainingAllowance - offeredAdditionalLoan\n }.restAfterNewLoan < 0" 10 | } 11 | } 12 | ], 13 | "type": "default", 14 | "id": "Form_0jn1poe", 15 | "exporter": {}, 16 | "schemaVersion": 12 17 | } 18 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/condition-external-variable.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "amount", 6 | "label": "Amount", 7 | "type": "number", 8 | "validate": { 9 | "min": 0, 10 | "max": 1000 11 | }, 12 | "conditional": { 13 | "hide": "=externalVariable" 14 | } 15 | }, 16 | { 17 | "label": "Text Field", 18 | "key": "textfield", 19 | "type": "textfield", 20 | "id": "Field", 21 | "conditional": { 22 | "hide": "=amount > 2" 23 | } 24 | } 25 | ], 26 | "type": "default", 27 | "id": "Form", 28 | "schemaVersion": 5 29 | } 30 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/condition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "amount", 6 | "label": "Amount", 7 | "type": "number", 8 | "validate": { 9 | "min": 0, 10 | "max": 1000 11 | } 12 | }, 13 | { 14 | "label": "Text Field", 15 | "type": "textfield", 16 | "id": "Field", 17 | "key": "text", 18 | "conditional": { 19 | "hide": "=amount <= 2" 20 | } 21 | } 22 | ], 23 | "type": "default", 24 | "id": "Form", 25 | "schemaVersion": 5 26 | } 27 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/custom/custom.css: -------------------------------------------------------------------------------- 1 | .fjs-container .fjs-button.custom-button[type='submit'] { 2 | color: var(--color-text-inverted); 3 | background-color: #10ad73; 4 | border-color: #10ad73; 5 | } 6 | 7 | .fjs-container .fjs-button.custom-button[type='reset'] { 8 | color: #10ad73; 9 | background-color: transparent; 10 | border-color: #10ad73; 11 | } 12 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/customField.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "key": "creditor", 5 | "label": "Creditor", 6 | "type": "textfield", 7 | "validate": { 8 | "required": true 9 | } 10 | }, 11 | { 12 | "key": "amount", 13 | "type": "range", 14 | "label": "Amount", 15 | "min": 0, 16 | "max": 100, 17 | "step": 5 18 | }, 19 | { 20 | "type": "button", 21 | "action": "submit", 22 | "label": "Submit" 23 | } 24 | ], 25 | "type": "default" 26 | } 27 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/cyclical-expressions.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "computeOn": "change", 5 | "type": "expression", 6 | "id": "Field_exprA", 7 | "key": "exprA", 8 | "expression": "=exprB + 10" 9 | }, 10 | { 11 | "computeOn": "change", 12 | "type": "expression", 13 | "id": "Field_exprB", 14 | "key": "exprB", 15 | "expression": "=exprA * 2" 16 | } 17 | ], 18 | "type": "default", 19 | "id": "Form_cyclic_example", 20 | "executionPlatform": "Camunda Cloud", 21 | "executionPlatformVersion": "8.5.0", 22 | "exporter": { 23 | "name": "Camunda Modeler", 24 | "version": "5.22.0" 25 | }, 26 | "schemaVersion": 16 27 | } 28 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/descriptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "textfield", 6 | "description": "{{ foo }} and {{ bar }}", 7 | "key": "template" 8 | }, 9 | { 10 | "type": "textfield", 11 | "description": "=description_var", 12 | "key": "expression" 13 | } 14 | ], 15 | "type": "default" 16 | } 17 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/documentPreview.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "label": "Case {{case_id}} documents", 5 | "type": "documentPreview", 6 | "id": "Field_0wy8tws", 7 | "dataSource": "=my_documents" 8 | } 9 | ], 10 | "$schema": "../../../form-json-schema/resources/schema.json", 11 | "type": "default", 12 | "id": "Form_1bd2k8m", 13 | "schemaVersion": 18 14 | } 15 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/dynamic.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "product", 6 | "label": "Product", 7 | "type": "radio", 8 | "valuesKey": "xyzData" 9 | }, 10 | { 11 | "key": "mailto", 12 | "label": "Email Summary To", 13 | "type": "checklist", 14 | "valuesKey": "xyzData" 15 | }, 16 | { 17 | "key": "language", 18 | "label": "Language", 19 | "type": "select", 20 | "valuesKey": "xyzData" 21 | }, 22 | { 23 | "key": "tags", 24 | "label": "Taglist", 25 | "type": "taglist", 26 | "valuesKey": "xyzData" 27 | } 28 | ], 29 | "type": "default" 30 | } 31 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/expression-external-variable.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "source": "=logo", 6 | "type": "image" 7 | }, 8 | { 9 | "alt": "=alt", 10 | "type": "image" 11 | }, 12 | { 13 | "alt": "This is just an image", 14 | "type": "image" 15 | }, 16 | { 17 | "text": "=myText", 18 | "type": "text" 19 | } 20 | ], 21 | "type": "default", 22 | "id": "Form", 23 | "schemaVersion": 6 24 | } 25 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/expressionField.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Form_1lywnv3", 3 | "components": [ 4 | { 5 | "computeOn": "change", 6 | "label": "Expression", 7 | "type": "expression", 8 | "layout": { 9 | "row": "Row_0duk8pr", 10 | "columns": null 11 | }, 12 | "id": "Field_01dikzb", 13 | "key": "exp_expression", 14 | "expression": "=flatten(data[selected = filter].dates)" 15 | } 16 | ], 17 | "type": "default" 18 | } 19 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/hidden-fields-conditional.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "label": "a", 6 | "type": "checkbox", 7 | "key": "a" 8 | }, 9 | { 10 | "label": "b", 11 | "type": "checkbox", 12 | "key": "b" 13 | }, 14 | { 15 | "label": "c", 16 | "type": "checkbox", 17 | "key": "c", 18 | "conditional": { 19 | "hide": "=a" 20 | } 21 | }, 22 | { 23 | "text": "# Text", 24 | "type": "text", 25 | "conditional": { 26 | "hide": "=b and c" 27 | } 28 | } 29 | ], 30 | "type": "default", 31 | "id": "Form_0jn1poe", 32 | "exporter": {}, 33 | "schemaVersion": 6 34 | } 35 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/hidden-fields-expression.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "label": "a", 6 | "type": "checkbox", 7 | "key": "a" 8 | }, 9 | { 10 | "label": "b", 11 | "type": "textfield", 12 | "key": "b" 13 | }, 14 | { 15 | "label": "c", 16 | "type": "textfield", 17 | "key": "c", 18 | "conditional": { 19 | "hide": "=a" 20 | } 21 | }, 22 | { 23 | "type": "image", 24 | "alt": "=b + c", 25 | "source": "https://" 26 | } 27 | ], 28 | "type": "default", 29 | "id": "Form_0z44jiv", 30 | "exporter": {}, 31 | "schemaVersion": 6 32 | } 33 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/html.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "type": "default", 4 | "components": [ 5 | { 6 | "type": "html", 7 | "content": "

{{heading}}

\n

{{description}}

" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/images.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "source": "=logo_expression", 6 | "type": "image" 7 | }, 8 | { 9 | "alt": "=alt_expression", 10 | "type": "image" 11 | }, 12 | { 13 | "source": "{{ logo_template }}", 14 | "type": "image" 15 | }, 16 | { 17 | "alt": "{{ alt_template }}", 18 | "type": "image" 19 | } 20 | ], 21 | "type": "default" 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/labels.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "textfield", 6 | "label": "{{ foo }} and {{ bar }}", 7 | "key": "template" 8 | }, 9 | { 10 | "type": "textfield", 11 | "label": "=label_var", 12 | "key": "expression" 13 | } 14 | ], 15 | "type": "default" 16 | } 17 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/other.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "text", 6 | "text": "# File an Invoice\n\nAdd your invoice details below." 7 | }, 8 | { 9 | "key": "creditor", 10 | "label": "Creditor", 11 | "type": "textfield", 12 | "validate": { 13 | "required": true 14 | } 15 | }, 16 | { 17 | "description": "An invoice number in the format: C-123.", 18 | "key": "invoiceNumber", 19 | "label": "Invoice Number", 20 | "type": "textfield", 21 | "validate": { 22 | "pattern": "^C-[0-9]+$" 23 | } 24 | }, 25 | { 26 | "label": "Submit", 27 | "type": "button" 28 | } 29 | ], 30 | "type": "default" 31 | } 32 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/readonly-expression.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "amount", 6 | "label": "Amount", 7 | "type": "number", 8 | "readonly": "=foo + bar = 2" 9 | }, 10 | { 11 | "label": "Text Field", 12 | "type": "textfield", 13 | "id": "Field", 14 | "key": "text", 15 | "readonly": true 16 | } 17 | ], 18 | "type": "default", 19 | "id": "Form", 20 | "schemaVersion": 9 21 | } 22 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/render/components/form-fields/Default.spec.js: -------------------------------------------------------------------------------- 1 | import { Default } from '../../../../../src/render/components/form-fields/Default'; 2 | 3 | describe('Default', function () { 4 | it('#create', function () { 5 | // assume 6 | const { config } = Default; 7 | expect(config.type).to.eql('default'); 8 | expect(config.label).not.to.exist; 9 | expect(config.keyed).to.be.false; 10 | 11 | // when 12 | const field = config.create(); 13 | 14 | // then 15 | expect(field).to.eql({ 16 | components: [], 17 | }); 18 | 19 | // but when 20 | const customField = config.create({ 21 | custom: true, 22 | }); 23 | 24 | // then 25 | expect(customField).to.contain({ 26 | custom: true, 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/render/components/helper/index.js: -------------------------------------------------------------------------------- 1 | import { FormContext, LocalExpressionContext } from '../../../../../src/render/context'; 2 | 3 | import { createMockInjector } from './mocks'; 4 | 5 | export const MockFormContext = (props) => { 6 | const { options = {}, services = {}, formId = 'foo' } = props; 7 | 8 | const formContext = { 9 | getService: (type, strict) => createMockInjector(services, options).get(type, strict), 10 | formId, 11 | }; 12 | 13 | const data = options.data || options.initialData || {}; 14 | 15 | const localExpressionContext = { 16 | data, 17 | parent: null, 18 | this: data, 19 | i: [], 20 | }; 21 | 22 | return ( 23 | 24 | {props.children} 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/ships-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "text": "#### Ship Name:\n {{shipsForSale[item.name = selectedShip].name}}\n#### Ship Description: \n {{shipsForSale[item.name = selectedShip].description}}\n#### Ship Price: \n {{shipsForSale[item.name = selectedShip].purchasePrice}}\n#### Frame:\n {{shipsForSale[item.name = selectedShip].frame.name}}\n#### Reactor:\n {{shipsForSale[item.name = selectedShip].reactor.name}}\n#### Engine:\n {{shipsForSale[item.name = selectedShip].engine.name}}\n#### Moduels:\n {{shipsForSale[item.name = selectedShip].modules.name}}\n", 6 | "type": "text", 7 | "layout": { 8 | "row": "Row_1oxhkbh", 9 | "columns": null 10 | }, 11 | "id": "Field_10y8si5", 12 | "conditional": { 13 | "hide": "=selectedShip = null" 14 | } 15 | } 16 | ], 17 | "schemaVersion": 9, 18 | "type": "default", 19 | "id": "Form_0ysvmoe" 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/stress.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "id": "Form_1", 4 | "type": "default", 5 | "components": [ 6 | { 7 | "id": "Field_9", 8 | "key": "taglist", 9 | "label": "Taglist Performance Test", 10 | "type": "taglist", 11 | "valuesKey": "largeDataset" 12 | }, 13 | { 14 | "id": "Field_10", 15 | "key": "select", 16 | "label": "Select Performance Test", 17 | "type": "select", 18 | "valuesKey": "largeDataset" 19 | }, 20 | { 21 | "id": "Field_11", 22 | "action": "submit", 23 | "label": "Submit", 24 | "type": "button" 25 | }, 26 | { 27 | "id": "Field_12", 28 | "action": "reset", 29 | "label": "Reset", 30 | "type": "button" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/template-variable-complex.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "text": "ConditionalExample: {{#if value > minimum}} Display conditionally {{display.subdisplay}} {{/if}}", 6 | "type": "text" 7 | }, 8 | { 9 | "text": "NestedLoopExample: {{#loop orgs}} {{#loop members}} {{_parent_._parent_.external1.question}} {{/loop}} {{_parent_.external2}} {{/loop}}", 10 | "type": "text" 11 | }, 12 | { 13 | "text": "AbiguousNamesExample: {{parent}} {{this}}", 14 | "type": "text" 15 | }, 16 | { 17 | "text": "OutOfScopeExample: {{_parent_.child.child2}}", 18 | "type": "text" 19 | } 20 | ], 21 | "type": "default", 22 | "id": "Form", 23 | "schemaVersion": 6 24 | } 25 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/template-variable.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "text": "=myText", 6 | "type": "text" 7 | }, 8 | { 9 | "text": "Just some text", 10 | "type": "text" 11 | }, 12 | { 13 | "text": "Some templated text: {{greeting}} {{name}} {{#if showAge}} {{age}} {{/if}} {{#loop hobbies}} {{this}}: {{experience}} Y{{/loop}}", 14 | "type": "text" 15 | } 16 | ], 17 | "type": "default", 18 | "id": "Form", 19 | "schemaVersion": 6 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/text-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "type": "text", 6 | "text": "# Employees\n{{#loop users}}\n## {{name}}\n*Currently {{age}} years old, contact* [@{{twitter}}]({{\"https://twitter.com/\" + twitter}})\n### Skills\n{{#loop skills}}\n- {{this}}\n{{/loop}}\n{{/loop}}\n# Some conditions\n{{#if count(users) > 1}}There are multiple users{{/if}}\n{{#if false}}This should not display{{/if}}\n{{#if true}}This should display{{/if}}\n*Some italic text*\n**Some bold text**\n" 7 | } 8 | ], 9 | "type": "default" 10 | } 11 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/validate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "number_expression", 6 | "type": "number", 7 | "validate": { 8 | "min": "=min", 9 | "max": "=max" 10 | } 11 | }, 12 | { 13 | "key": "textfield_expression", 14 | "type": "textfield", 15 | "validate": { 16 | "minLength": "=minLength", 17 | "maxLength": "=maxLength" 18 | } 19 | } 20 | ], 21 | "type": "default" 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/spec/valuesExpression.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../form-json-schema/resources/schema.json", 3 | "components": [ 4 | { 5 | "key": "product", 6 | "label": "Product", 7 | "type": "radio", 8 | "values": [] 9 | }, 10 | { 11 | "key": "mailto", 12 | "label": "Email Summary To", 13 | "type": "checklist", 14 | "valuesExpression": "=myList" 15 | }, 16 | { 17 | "key": "foo", 18 | "type": "taglist", 19 | "valuesExpression": "=concatenate(myList2,myList3,myList4)" 20 | } 21 | ], 22 | "type": "default" 23 | } 24 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/test.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,600;1,400&display=swap'); 2 | 3 | body { 4 | font-family: sans-serif; 5 | font-size: 16px; 6 | margin: 1rem; 7 | } 8 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/testBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | -------------------------------------------------------------------------------- /packages/form-js-viewer/test/theme.scss: -------------------------------------------------------------------------------- 1 | @use '@carbon/styles'; 2 | @use '@carbon/styles/scss/themes'; 3 | @use '@carbon/styles/scss/theme'; 4 | 5 | .cds--g10 { 6 | @include theme.theme(themes.$g10); 7 | } 8 | 9 | .cds--g100 { 10 | @include theme.theme(themes.$g100); 11 | } 12 | -------------------------------------------------------------------------------- /packages/form-js/src/editor.js: -------------------------------------------------------------------------------- 1 | export * from '@bpmn-io/form-js-editor'; 2 | -------------------------------------------------------------------------------- /packages/form-js/src/index.js: -------------------------------------------------------------------------------- 1 | export * from './viewer'; 2 | export * from './editor'; 3 | export * from './playground'; 4 | -------------------------------------------------------------------------------- /packages/form-js/src/playground.js: -------------------------------------------------------------------------------- 1 | export { Playground as FormPlayground } from '@bpmn-io/form-js-playground'; 2 | -------------------------------------------------------------------------------- /packages/form-js/src/viewer.js: -------------------------------------------------------------------------------- 1 | export * from '@bpmn-io/form-js-viewer'; 2 | -------------------------------------------------------------------------------- /packages/form-js/test/coverageBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | 5 | const allSources = require.context('../src', true, /.*\.js$/); 6 | 7 | allSources.keys().forEach(allSources); 8 | -------------------------------------------------------------------------------- /packages/form-js/test/test.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | font-size: 16px; 4 | margin: 1rem; 5 | } 6 | 7 | h1 { 8 | margin: 0 0 1rem 0; 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-js/test/testBundle.js: -------------------------------------------------------------------------------- 1 | const allTests = require.context('.', true, /.spec\.js$/); 2 | 3 | allTests.keys().forEach(allTests); 4 | -------------------------------------------------------------------------------- /packages/form-json-schema/.gitignore: -------------------------------------------------------------------------------- 1 | resources -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/appearance", 4 | "type": "object", 5 | "description": "Changes the visual appearance of the form field.", 6 | "properties": { 7 | "prefixAdorner": { 8 | "$id": "/#component/appearance/prefixAdorner", 9 | "type": "string", 10 | "description": "Adds an appendage before the input." 11 | }, 12 | "suffixAdorner": { 13 | "$id": "/#component/appearance/suffixAdorner", 14 | "type": "string", 15 | "description": "Adds an appendage after the input." 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/columns.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/columns", 4 | "type": "array", 5 | "description": "Columns of a table component", 6 | "items": { 7 | "type": "object", 8 | "properties": { 9 | "label": { 10 | "$id": "#/component/columns/label", 11 | "type": "string", 12 | "description": "Label of the column" 13 | }, 14 | "key": { 15 | "$id": "#/component/values/key", 16 | "type": "string", 17 | "description": "Key of the column" 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/conditional.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/conditional", 4 | "type": "object", 5 | "description": "Information of a form field related to conditional rendering.", 6 | "properties": { 7 | "hide": { 8 | "$id": "/#component/conditional/hide", 9 | "type": ["string", "boolean"], 10 | "description": "Expression to hide the form field." 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/exporter.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/exporter", 4 | "description": "The exporter tool of a form", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "$id": "#/exporter/name", 9 | "description": "The name of the exporter tool", 10 | "type": "string" 11 | }, 12 | "version": { 13 | "$id": "#/exporter/version", 14 | "description": "The version of the exporter tool", 15 | "type": "string" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/field-types/containers.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "type": { 4 | "enum": ["group", "dynamiclist"] 5 | } 6 | }, 7 | "required": ["type"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/field-types/inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "type": { 4 | "enum": [ 5 | "checkbox", 6 | "checklist", 7 | "datetime", 8 | "number", 9 | "radio", 10 | "select", 11 | "taglist", 12 | "textfield", 13 | "textarea", 14 | "expression", 15 | "filepicker" 16 | ] 17 | } 18 | }, 19 | "required": ["type"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/field-types/multi-inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "type": { 4 | "enum": ["checklist", "radio", "select", "taglist"] 5 | } 6 | }, 7 | "required": ["type"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/field-types/presentation-components.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "type": { 4 | "enum": ["table", "documentPreview"] 5 | } 6 | }, 7 | "required": ["type"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/layout", 4 | "type": "object", 5 | "description": "Layout related information of a form field.", 6 | "properties": { 7 | "row": { 8 | "$id": "/#component/layout/row", 9 | "type": ["string", "null"], 10 | "description": "Row in which a form field is placed." 11 | }, 12 | "columns": { 13 | "$id": "/#component/layout/columns", 14 | "type": ["integer", "null"], 15 | "description": "Space the field will use inside its row. No value means it will automatically adjust to available space in the row.", 16 | "minimum": 2, 17 | "maximum": 16 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/properties", 4 | "type": "object", 5 | "description": "Custom properties of a form field." 6 | } 7 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/rules/rules-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "allOf": [ 4 | { 5 | "if": { 6 | "properties": { 7 | "type": { 8 | "const": "default" 9 | } 10 | }, 11 | "required": ["type"] 12 | }, 13 | "then": { 14 | "properties": { 15 | "layout": false, 16 | "conditional": false, 17 | "properties": false 18 | } 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/type.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/type", 4 | "description": "The type of a form field.", 5 | "enum": [ 6 | "textfield", 7 | "number", 8 | "datetime", 9 | "textarea", 10 | "checkbox", 11 | "radio", 12 | "select", 13 | "checklist", 14 | "taglist", 15 | "image", 16 | "text", 17 | "html", 18 | "button", 19 | "spacer", 20 | "group", 21 | "dynamiclist", 22 | "separator", 23 | "table", 24 | "iframe", 25 | "expression", 26 | "filepicker", 27 | "documentPreview" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/defs/values.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "#/component/values", 4 | "type": "array", 5 | "description": "Static options of a form field", 6 | "items": { 7 | "type": "object", 8 | "properties": { 9 | "label": { 10 | "$id": "#/component/values/label", 11 | "type": "string", 12 | "description": "Label of the option" 13 | }, 14 | "value": { 15 | "$id": "#/component/values/value", 16 | "type": "string", 17 | "description": "Value of the option" 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/form-json-schema/src/error-messages.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/accept-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'textfield_g35o3e', 7 | accept: '.png,.jpg', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/accept', 15 | keyword: 'false schema', 16 | message: 'boolean schema is false', 17 | params: {}, 18 | schemaPath: '#/properties/components/items/allOf/1/allOf/21/then/properties/accept/false schema', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/21/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/action-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | action: 'submit', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/action', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/3/then/properties/action/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/3/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/alt-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | alt: 'alt', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/alt', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/4/then/properties/alt/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/4/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/appearance-prefixAdorner-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | key: 'text', 7 | appearance: { 8 | prefixAdorner: 'prefix', 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/appearance/prefixAdorner', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/11/then/properties/appearance/properties/prefixAdorner/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/11/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/appearance-suffixAdorner-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | key: 'text', 7 | appearance: { 8 | suffixAdorner: 'suffix', 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/appearance/suffixAdorner', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/11/then/properties/appearance/properties/suffixAdorner/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/11/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/columns-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | key: 'text', 7 | columns: [], 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/columns', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/then/properties/columns/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/columnsExpression-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | key: 'text', 7 | columnsExpression: '=foo', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/columnsExpression', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/then/properties/columnsExpression/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/components-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | components: [], 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/components', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/then/properties/components/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/conditional-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [], 4 | conditional: {}, 5 | }; 6 | 7 | export const errors = [ 8 | { 9 | instancePath: '/conditional', 10 | schemaPath: '#/allOf/0/allOf/0/then/properties/conditional/false schema', 11 | keyword: 'false schema', 12 | params: {}, 13 | message: 'boolean schema is false', 14 | }, 15 | { 16 | instancePath: '', 17 | schemaPath: '#/allOf/0/allOf/0/if', 18 | keyword: 'if', 19 | params: { failingKeyword: 'then' }, 20 | message: 'must match "then" schema', 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/content-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | content: '

Some HTML

', 7 | key: 'abc', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/content', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/20/then/properties/content/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/20/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/dataSource-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textarea', 6 | key: 'text', 7 | dataSource: 'inputVariable', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/dataSource', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/19/then/properties/dataSource/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/19/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/dateLabel-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | dateLabel: 'date', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/dateLabel', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/dateLabel/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/decimalDigits-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | decimalDigits: 2, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/decimalDigits', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/then/properties/decimalDigits/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/defaultValue-no-array.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'checklist', 6 | key: 'list', 7 | defaultValue: 'foo', 8 | values: [], 9 | }, 10 | ], 11 | }; 12 | 13 | export const errors = [ 14 | { 15 | instancePath: '/components/0/defaultValue', 16 | schemaPath: '#/properties/components/items/allOf/2/allOf/3/then/properties/defaultValue/type', 17 | keyword: 'type', 18 | params: { type: 'array' }, 19 | message: 'must be array', 20 | }, 21 | { 22 | instancePath: '/components/0', 23 | schemaPath: '#/properties/components/items/allOf/2/allOf/3/if', 24 | keyword: 'if', 25 | params: { failingKeyword: 'then' }, 26 | message: 'must match "then" schema', 27 | }, 28 | ]; 29 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/defaultValue-no-boolean.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'checkbox', 6 | key: 'checkbox', 7 | defaultValue: 'foo', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/defaultValue', 15 | schemaPath: '#/properties/components/items/allOf/2/allOf/0/then/properties/defaultValue/type', 16 | keyword: 'type', 17 | params: { type: 'boolean' }, 18 | message: 'must be boolean', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/2/allOf/0/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/defaultValue-no-string-or-number.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'number', 7 | defaultValue: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/defaultValue', 15 | schemaPath: '#/properties/components/items/allOf/2/allOf/2/then/properties/defaultValue/type', 16 | keyword: 'type', 17 | params: { type: ['number', 'string'] }, 18 | message: 'must be number,string', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/2/allOf/2/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/defaultValue-no-string.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'textfield', 7 | defaultValue: 2, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/defaultValue', 15 | schemaPath: '#/properties/components/items/allOf/2/allOf/1/then/properties/defaultValue/type', 16 | keyword: 'type', 17 | params: { type: 'string' }, 18 | message: 'must be string', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/2/allOf/1/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/description-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | description: 'text', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/description', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/then/properties/description/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/disabled-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | disabled: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/disabled', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/then/properties/disabled/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/disallowPassedDates-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | disallowPassedDates: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/disallowPassedDates', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/disallowPassedDates/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/documentPreview.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'documentPreview', 6 | dataSource: '=someSource', 7 | label: 'My documents', 8 | maxHeight: 100, 9 | }, 10 | ], 11 | }; 12 | 13 | export const errors = null; 14 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/dynamic-list-properties.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'dynamiclist', 6 | path: 'myGroup', 7 | isRepeating: true, 8 | defaultRepetitions: 5, 9 | allowAddRemove: true, 10 | disableCollapse: false, 11 | nonCollapsedItems: 3, 12 | }, 13 | ], 14 | }; 15 | 16 | export const errors = null; 17 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/dynamiclists.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | id: 'id_dynamic_list', 6 | path: 'dynamic', 7 | label: 'Dynamic list', 8 | type: 'dynamiclist', 9 | components: [], 10 | }, 11 | { 12 | id: 'id_dynamic_list_2', 13 | path: 'dynamic2', 14 | label: 'Dynamic list', 15 | type: 'dynamiclist', 16 | showOutline: true, 17 | }, 18 | { 19 | id: 'id_dynamic_list_parent', 20 | path: 'dynamicParent', 21 | label: 'Dynamic list parent', 22 | type: 'dynamiclist', 23 | components: [ 24 | { 25 | id: 'id_dynamic_list_child', 26 | path: 'dynamicChild', 27 | label: 'Dynamic list child', 28 | type: 'dynamiclist', 29 | components: [], 30 | }, 31 | ], 32 | }, 33 | ], 34 | }; 35 | 36 | export const errors = null; 37 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/filepicker.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'filepicker', 6 | key: 'filepicker', 7 | accept: '.png,.jpg', 8 | multiple: true, 9 | }, 10 | ], 11 | }; 12 | 13 | export const errors = null; 14 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/height-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | height: 60, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/height', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/13/then/properties/height/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/13/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/iframe.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'iframe', 6 | label: 'The bpmn-io web page', 7 | url: 'https://bpmn.io/', 8 | height: 400, 9 | security: { 10 | allowSameOrigin: true, 11 | fullscreen: true, 12 | geolocation: true, 13 | camera: true, 14 | microphone: true, 15 | allowForms: true, 16 | allowModals: true, 17 | allowPopups: true, 18 | allowTopNavigation: true, 19 | }, 20 | }, 21 | ], 22 | }; 23 | 24 | export const errors = null; 25 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/increment-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | increment: '2', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/increment', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/then/properties/increment/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/increment.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'increment_string', 7 | increment: '20', 8 | }, 9 | { 10 | type: 'number', 11 | key: 'increment_number', 12 | increment: 20, 13 | }, 14 | { 15 | type: 'number', 16 | key: 'increment_float', 17 | increment: 20.3, 18 | }, 19 | ], 20 | }; 21 | 22 | export const errors = null; 23 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/key-invalid.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'textfield_1', 7 | }, 8 | { 9 | type: 'textfield', 10 | key: 'textfield_2.foo', 11 | }, 12 | { 13 | type: 'textfield', 14 | key: 'textfield_3.', 15 | }, 16 | ], 17 | }; 18 | 19 | export const errors = [ 20 | { 21 | instancePath: '/components/2/key', 22 | schemaPath: '#/properties/components/items/properties/key/pattern', 23 | keyword: 'pattern', 24 | params: { pattern: '^\\w+(\\.\\w+)*$' }, 25 | message: 'must match pattern "^\\w+(\\.\\w+)*$"', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/key-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | key: 'text', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/key', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/16/then/properties/key/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/16/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/label-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | label: 'text', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/label', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/1/then/properties/label/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/1/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/layout-empty-row.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'firstName', 7 | layout: { 8 | row: null, 9 | columns: 12, 10 | }, 11 | }, 12 | ], 13 | }; 14 | 15 | export const errors = null; 16 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/layout-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [], 4 | layout: {}, 5 | }; 6 | 7 | export const errors = [ 8 | { 9 | instancePath: '/layout', 10 | schemaPath: '#/allOf/0/allOf/0/then/properties/layout/false schema', 11 | keyword: 'false schema', 12 | params: {}, 13 | message: 'boolean schema is false', 14 | }, 15 | { 16 | instancePath: '', 17 | schemaPath: '#/allOf/0/allOf/0/if', 18 | keyword: 'if', 19 | params: { failingKeyword: 'then' }, 20 | message: 'must match "then" schema', 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/maxHeight-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'textfield_g35o3e', 7 | maxHeight: 100, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/maxHeight', 15 | keyword: 'false schema', 16 | message: 'boolean schema is false', 17 | params: {}, 18 | schemaPath: '#/properties/components/items/allOf/1/allOf/23/then/properties/maxHeight/false schema', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/23/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/multiple-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'textfield_g35o3e', 7 | multiple: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/multiple', 15 | keyword: 'false schema', 16 | message: 'boolean schema is false', 17 | params: {}, 18 | schemaPath: '#/properties/components/items/allOf/1/allOf/21/then/properties/multiple/false schema', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/21/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/no-action.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'button', 6 | }, 7 | ], 8 | }; 9 | 10 | export const errors = null; 11 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/no-key.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | }, 7 | { 8 | type: 'text', 9 | }, 10 | ], 11 | }; 12 | 13 | export const errors = [ 14 | { 15 | instancePath: '/components/0', 16 | schemaPath: '#/properties/components/items/allOf/0/allOf/0/then/required', 17 | keyword: 'required', 18 | params: { missingProperty: 'key' }, 19 | message: "must have required property 'key'", 20 | }, 21 | { 22 | instancePath: '/components/0', 23 | schemaPath: '#/properties/components/items/allOf/0/allOf/0/if', 24 | keyword: 'if', 25 | params: { failingKeyword: 'then' }, 26 | message: 'must match "then" schema', 27 | }, 28 | ]; 29 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/no-subtype.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'datetime', 6 | key: 'date', 7 | }, 8 | ], 9 | }; 10 | 11 | export const errors = null; 12 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/no-values-property.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'select', 6 | key: 'select', 7 | }, 8 | ], 9 | }; 10 | 11 | export const errors = null; 12 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixtures", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/path-invalid.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'group', 6 | path: 'group_1', 7 | }, 8 | { 9 | type: 'group', 10 | path: 'group_2.foo', 11 | }, 12 | { 13 | type: 'group', 14 | path: 'group_3.', 15 | }, 16 | { 17 | type: 'group', 18 | path: '', 19 | }, 20 | ], 21 | }; 22 | 23 | export const errors = [ 24 | { 25 | instancePath: '/components/2/path', 26 | schemaPath: '#/properties/components/items/properties/path/pattern', 27 | keyword: 'pattern', 28 | params: { pattern: '^(\\w+(\\.\\w+)*)*$' }, 29 | message: 'must match pattern "^(\\w+(\\.\\w+)*)*$"', 30 | }, 31 | ]; 32 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/path-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | path: 'foobar', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/path', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/then/properties/path/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/properties-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [], 4 | properties: {}, 5 | }; 6 | 7 | export const errors = [ 8 | { 9 | instancePath: '/properties', 10 | schemaPath: '#/allOf/0/allOf/0/then/properties/properties/false schema', 11 | keyword: 'false schema', 12 | params: {}, 13 | message: 'boolean schema is false', 14 | }, 15 | { 16 | instancePath: '', 17 | schemaPath: '#/allOf/0/allOf/0/if', 18 | keyword: 'if', 19 | params: { failingKeyword: 'then' }, 20 | message: 'must match "then" schema', 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/readonly-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | readonly: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/readonly', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/then/properties/readonly/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/rowCount-no-number.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'table', 6 | rowCount: true, 7 | columns: [], 8 | dataSource: 'inputVariable', 9 | }, 10 | ], 11 | }; 12 | 13 | export const errors = [ 14 | { 15 | instancePath: '/components/0/rowCount', 16 | schemaPath: '#/properties/components/items/properties/rowCount/type', 17 | keyword: 'type', 18 | params: { type: 'number' }, 19 | message: 'must be number', 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/rowCount-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | rowCount: 10, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/rowCount', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/then/properties/rowCount/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/17/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/schemaVersion-not-supported.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [], 4 | schemaVersion: 19, 5 | }; 6 | 7 | export const errors = [ 8 | { 9 | instancePath: '/schemaVersion', 10 | schemaPath: '#/properties/schemaVersion/maximum', 11 | keyword: 'maximum', 12 | params: { comparison: '<=', limit: 18 }, 13 | message: 'must be <= 18', 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/searchable-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | searchable: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/searchable', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/7/then/properties/searchable/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/7/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/serializeToString-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | serializeToString: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/serializeToString', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/then/properties/serializeToString/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/showOutline-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | showOutline: false, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/showOutline', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/then/properties/showOutline/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/source-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | source: 'image', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/source', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/4/then/properties/source/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/4/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/subtype-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | subtype: 'date', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/subtype', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/subtype/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/text-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'button', 6 | text: 'Click me', 7 | action: 'submit', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/text', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/0/then/properties/text/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/0/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/timeInterval-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | timeInterval: 2, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/timeInterval', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/timeInterval/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/timeLabel-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | timeLabel: 'date', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/timeLabel', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/timeLabel/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/timeSerializingFormat-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | timeSerializingFormat: 'utc_offset', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/timeSerializingFormat', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/timeSerializingFormat/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/use24h-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | use24h: true, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/use24h', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/then/properties/use24h/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/5/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-max-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'text', 7 | validate: { 8 | min: 2, 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/min', 17 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/then/properties/validate/properties/min/false schema', 18 | keyword: 'false schema', 19 | params: {}, 20 | message: 'boolean schema is false', 21 | }, 22 | { 23 | instancePath: '/components/0', 24 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/if', 25 | keyword: 'if', 26 | params: { failingKeyword: 'then' }, 27 | message: 'must match "then" schema', 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-maxLength-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'number', 7 | validate: { 8 | maxLength: 2, 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/maxLength', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/10/then/properties/validate/properties/maxLength/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/10/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-min-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'text', 7 | validate: { 8 | max: 2, 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/max', 17 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/then/properties/validate/properties/max/false schema', 18 | keyword: 'false schema', 19 | params: {}, 20 | message: 'boolean schema is false', 21 | }, 22 | { 23 | instancePath: '/components/0', 24 | schemaPath: '#/properties/components/items/allOf/1/allOf/6/if', 25 | keyword: 'if', 26 | params: { failingKeyword: 'then' }, 27 | message: 'must match "then" schema', 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-minLength-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'number', 7 | validate: { 8 | minLength: 2, 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/minLength', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/10/then/properties/validate/properties/minLength/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/10/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | validate: {}, 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/validate', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/then/properties/validate/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/2/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-pattern-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'number', 7 | validate: { 8 | pattern: '^[0-9]+$', 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/pattern', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/9/then/properties/validate/properties/pattern/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/9/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-validationType-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'number', 6 | key: 'number', 7 | validate: { 8 | validationType: 'email', 9 | }, 10 | }, 11 | ], 12 | }; 13 | 14 | export const errors = [ 15 | { 16 | instancePath: '/components/0/validate/validationType', 17 | schemaPath: 18 | '#/properties/components/items/allOf/1/allOf/9/then/properties/validate/properties/validationType/false schema', 19 | keyword: 'false schema', 20 | params: {}, 21 | message: 'boolean schema is false', 22 | }, 23 | { 24 | instancePath: '/components/0', 25 | schemaPath: '#/properties/components/items/allOf/1/allOf/9/if', 26 | keyword: 'if', 27 | params: { failingKeyword: 'then' }, 28 | message: 'must match "then" schema', 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/validate-validationType.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'textfield', 6 | key: 'field_email', 7 | validate: { 8 | validationType: 'email', 9 | }, 10 | }, 11 | { 12 | type: 'textfield', 13 | key: 'field_phone', 14 | validate: { 15 | validationType: 'phone', 16 | }, 17 | }, 18 | { 19 | type: 'textfield', 20 | key: 'field_custom', 21 | validate: { 22 | validationType: 'custom', 23 | }, 24 | }, 25 | { 26 | type: 'textfield', 27 | key: 'field_empty', 28 | validate: { 29 | validationType: '', 30 | }, 31 | }, 32 | { 33 | type: 'textfield', 34 | key: 'field_undefined', 35 | validate: {}, 36 | }, 37 | ], 38 | }; 39 | 40 | export const errors = null; 41 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/values-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | values: [], 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/values', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/then/properties/values/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/valuesExpression-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | valuesExpression: '=foo', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/valuesExpression', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/then/properties/valuesExpression/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/valuesKey-invalid.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'select', 6 | key: 'select', 7 | valuesKey: 'foo bar', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/valuesKey', 15 | schemaPath: '#/properties/components/items/properties/valuesKey/pattern', 16 | keyword: 'pattern', 17 | params: { pattern: '^[^\\s]*$' }, 18 | message: 'must match pattern "^[^\\s]*$"', 19 | }, 20 | ]; 21 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/valuesKey-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | valuesKey: 'foo', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/valuesKey', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/then/properties/valuesKey/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/8/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/verticalAlignment-invalid.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'dynamiclist', 6 | verticalAlignment: 'top', 7 | }, 8 | ], 9 | }; 10 | 11 | export const errors = [ 12 | { 13 | instancePath: '/components/0/verticalAlignment', 14 | schemaPath: '#/properties/components/items/properties/verticalAlignment/enum', 15 | keyword: 'enum', 16 | params: { allowedValues: ['start', 'center', 'end'] }, 17 | message: 'must be equal to one of the allowed values', 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/fixtures/verticalAlignment-not-allowed.js: -------------------------------------------------------------------------------- 1 | export const form = { 2 | type: 'default', 3 | components: [ 4 | { 5 | type: 'text', 6 | text: 'text', 7 | verticalAlignment: 'start', 8 | }, 9 | ], 10 | }; 11 | 12 | export const errors = [ 13 | { 14 | instancePath: '/components/0/verticalAlignment', 15 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/then/properties/verticalAlignment/false schema', 16 | keyword: 'false schema', 17 | params: {}, 18 | message: 'boolean schema is false', 19 | }, 20 | { 21 | instancePath: '/components/0', 22 | schemaPath: '#/properties/components/items/allOf/1/allOf/14/if', 23 | keyword: 'if', 24 | params: { failingKeyword: 'then' }, 25 | message: 'must match "then" schema', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /packages/form-json-schema/test/spec/schema.spec.js: -------------------------------------------------------------------------------- 1 | const { default: Ajv } = require('ajv'); 2 | const { expect } = require('chai'); 3 | 4 | const schema = require('../../resources/schema.json'); 5 | 6 | describe('schema validation', function () { 7 | it('should be valid', function () { 8 | // given 9 | const ajv = new Ajv(); 10 | 11 | // when 12 | const valid = ajv.validateSchema(schema); 13 | 14 | // then 15 | expect(valid).to.be.true; 16 | expect(valid.errors).to.not.exist; 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from '@playwright/test'; 2 | 3 | const IS_CI = !!process.env.CI; 4 | 5 | export default defineConfig({ 6 | testDir: './e2e', 7 | fullyParallel: true, 8 | forbidOnly: IS_CI, 9 | retries: IS_CI ? 2 : 0, 10 | workers: IS_CI ? 1 : undefined, 11 | reporter: 'html', 12 | use: { 13 | actionTimeout: 0, 14 | baseURL: 'http://localhost:8080', 15 | trace: 'retain-on-failure', 16 | video: 'retain-on-failure', 17 | }, 18 | projects: [ 19 | { 20 | name: 'chromium', 21 | use: { ...devices['Desktop Chrome'] }, 22 | }, 23 | 24 | { 25 | name: 'firefox', 26 | use: { ...devices['Desktop Firefox'] }, 27 | }, 28 | 29 | { 30 | name: 'webkit', 31 | use: { ...devices['Desktop Safari'] }, 32 | }, 33 | ], 34 | }); 35 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>bpmn-io/renovate-config:recommended", ":automergeDigest"], 4 | "reviewers": ["team:hto-dev"], 5 | "baseBranches": ["main"] 6 | } 7 | -------------------------------------------------------------------------------- /tasks/stages/await-published: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | shopt -s inherit_errexit nullglob 5 | 6 | i=0 7 | pkg="$PKG" 8 | until [ $i -gt 10 ] 9 | do 10 | echo "Checking for $pkg in npm registry ($((i+1))/10)" 11 | info=$(npm info $pkg) 12 | if [[ "$info" != "" ]]; then 13 | echo "Found." 14 | break 15 | fi 16 | 17 | i=$((var+1)) 18 | sleep 15s 19 | done -------------------------------------------------------------------------------- /tasks/stages/update-demo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | shopt -s inherit_errexit nullglob 5 | 6 | # bumps form-js dependencies in bpmn-io-demo 7 | 8 | PWD="$(pwd)" 9 | WORKDIR="$(pwd)/tmp" 10 | CLONE_DIR="$WORKDIR/bpmn-io-demo" 11 | 12 | # create work dir 13 | mkdir -p "$WORKDIR" 14 | 15 | git clone --depth=1 "https://$BPMN_IO_TOKEN@github.com/$BPMN_IO_DEMO_ENDPOINT.git" "$CLONE_DIR" 16 | 17 | cd "$CLONE_DIR" 18 | 19 | npm install --save @bpmn-io/form-js@$TAG diagram-js@latest 20 | 21 | if [[ "x$SKIP_COMMIT" = "x" ]]; then 22 | 23 | git config user.email "$BPMN_IO_EMAIL" 24 | git config user.name "$BPMN_IO_USERNAME" 25 | git config push.default simple 26 | 27 | git add -A 28 | git commit -m "deps: bump to @bpmn-io/form-js@$TAG" 29 | git tag "form-js-$TAG" 30 | git push -q &2>/dev/null 31 | git push --tags -q &2>/dev/null 32 | else 33 | echo "Skipping commit (SKIP_COMMIT=$SKIP_COMMIT)" 34 | fi 35 | 36 | cd "$PWD" -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es2019", "dom"], 5 | "target": "es2019", 6 | "allowJs": true, 7 | "checkJs": true, 8 | "rootDir": ".", 9 | "strict": false, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "jsx": "react-jsx", 14 | "jsxImportSource": "preact", 15 | "noEmit": true 16 | }, 17 | "include": [ 18 | "packages/form-js-viewer/src", 19 | "packages/form-js-editor/src", 20 | "packages/form-js/src", 21 | "packages/form-js/test/spec", 22 | "packages/form-js-integration/src" 23 | ], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { resolve } from 'path'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | root: 'e2e', 7 | define: { 8 | global: 'window', // necessary to fix Dragula error: `global is not defined` 9 | }, 10 | build: { 11 | rollupOptions: { 12 | input: { 13 | main: resolve(__dirname, 'e2e/index.html'), 14 | theming: resolve(__dirname, 'e2e/theming/index.html'), 15 | carbon: resolve(__dirname, 'e2e/carbon/index.html'), 16 | }, 17 | }, 18 | }, 19 | }); 20 | --------------------------------------------------------------------------------