From 6a40c7210b65ebb745fd400d9e1e5d5a4fb7f10b Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Thu, 4 Aug 2022 14:23:14 +0200 Subject: [PATCH 1/2] feat(playground): emit `formPlayground.rendered` event --- packages/form-js-playground/src/Playground.js | 5 ++++ .../src/components/PlaygroundRoot.js | 11 ++++++++- .../test/spec/Playground.spec.js | 23 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/form-js-playground/src/Playground.js b/packages/form-js-playground/src/Playground.js index aae82f9b7..e76ed4156 100644 --- a/packages/form-js-playground/src/Playground.js +++ b/packages/form-js-playground/src/Playground.js @@ -59,6 +59,7 @@ export default function Playground(options) { data={ data } onStateChanged={ (_state) => state = _state } onInit={ _ref => ref = _ref } + emit={ emitter.emit } { ...rest } />, container @@ -85,6 +86,10 @@ export default function Playground(options) { return ref.setSchema(schema); }; + this.get = function(name, strict) { + return ref.get(name, strict); + }; + this.destroy = function() { this.emit('destroy'); }; diff --git a/packages/form-js-playground/src/components/PlaygroundRoot.js b/packages/form-js-playground/src/components/PlaygroundRoot.js index 3e643d2bc..0b5065aef 100644 --- a/packages/form-js-playground/src/components/PlaygroundRoot.js +++ b/packages/form-js-playground/src/components/PlaygroundRoot.js @@ -24,7 +24,8 @@ import './PlaygroundRoot.css'; export function PlaygroundRoot(props) { const { - editor: editorConfig = {} + editor: editorConfig = {}, + emit } = props; const { @@ -53,8 +54,10 @@ export function PlaygroundRoot(props) { const [ resultData, setResultData ] = useState(props.data || {}); + // pipe to playground API useEffect(() => { props.onInit({ + get: (name, strict) => formEditorRef.current.get(name, strict), setSchema: setInitialSchema }); }); @@ -90,6 +93,12 @@ export function PlaygroundRoot(props) { setSchema(formEditor.getSchema()); }); + formEditor.on('formEditor.rendered', () => { + + // notifiy interested parties after render + emit('formPlayground.rendered'); + }); + form.on('changed', event => { setResultData(event.data); }); diff --git a/packages/form-js-playground/test/spec/Playground.spec.js b/packages/form-js-playground/test/spec/Playground.spec.js index 434bd2a68..4a5a067ea 100644 --- a/packages/form-js-playground/test/spec/Playground.spec.js +++ b/packages/form-js-playground/test/spec/Playground.spec.js @@ -181,7 +181,30 @@ describe('playground', function() { // then expect(playground.getState().schema).to.deep.include(otherSchema); + }); + + + it('should emit ', async function() { + + // given + const spy = sinon.spy(); + await act(() => { + playground = new Playground({ + container, + schema + }); + }); + + playground.on('formPlayground.rendered', spy); + + const eventBus = playground.get('eventBus'); + + // when + eventBus.fire('formEditor.rendered'); + + // then + expect(spy).to.have.been.called; }); }); \ No newline at end of file From 69fba93016dbdfa5286eb4833d45bdb2126a74f0 Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Fri, 5 Aug 2022 14:38:05 +0200 Subject: [PATCH 2/2] feat(playground): support components to be rendered flexible Closes #292 --- packages/form-js-playground/package.json | 1 + packages/form-js-playground/src/Playground.js | 41 ++- .../src/components/PlaygroundRoot.js | 49 +++- .../test/spec/Playground.spec.js | 251 +++++++++++++++++- 4 files changed, 326 insertions(+), 16 deletions(-) diff --git a/packages/form-js-playground/package.json b/packages/form-js-playground/package.json index 81f0718bc..d94cf7a48 100644 --- a/packages/form-js-playground/package.json +++ b/packages/form-js-playground/package.json @@ -61,6 +61,7 @@ ], "devDependencies": { "css-loader": "^6.3.0", + "min-dash": "^3.8.1", "min-dom": "^3.2.1", "rollup-plugin-css-only": "^3.1.0", "style-loader": "^3.3.0" diff --git a/packages/form-js-playground/src/Playground.js b/packages/form-js-playground/src/Playground.js index e76ed4156..f807db7f3 100644 --- a/packages/form-js-playground/src/Playground.js +++ b/packages/form-js-playground/src/Playground.js @@ -8,10 +8,11 @@ import { PlaygroundRoot } from './components/PlaygroundRoot'; /** * @typedef { { - * container: Element, + * container?: Element, * schema: any, * data: any, * editor?: { inlinePropertiesPanel: Boolean } + * actions?: { display: Boolean } * } } FormPlaygroundOptions */ @@ -36,7 +37,9 @@ export default function Playground(options) { container.classList.add('fjs-pgl-parent'); - parent.appendChild(container); + if (parent) { + parent.appendChild(container); + } const handleDrop = fileDrop('Drop a form file', function(files) { const file = files[0]; @@ -51,6 +54,16 @@ export default function Playground(options) { } }); + const withRef = function(fn) { + return function(...args) { + if (!ref) { + throw new Error('Playground is not initialized.'); + } + + fn(...args); + }; + }; + container.addEventListener('dragover', handleDrop); render( @@ -93,4 +106,28 @@ export default function Playground(options) { this.destroy = function() { this.emit('destroy'); }; + + this.attachEditorContainer = withRef(function(node) { + return ref.attachEditorContainer(node); + }); + + this.attachPreviewContainer = withRef(function(node) { + return ref.attachFormContainer(node); + }); + + this.attachDataContainer = withRef(function(node) { + return ref.attachDataContainer(node); + }); + + this.attachResultContainer = withRef(function(node) { + return ref.attachResultContainer(node); + }); + + this.attachPaletteContainer = withRef(function(node) { + return ref.attachPaletteContainer(node); + }); + + this.attachPropertiesPanelContainer = withRef(function(node) { + return ref.attachPropertiesPanelContainer(node); + }); } \ No newline at end of file diff --git a/packages/form-js-playground/src/components/PlaygroundRoot.js b/packages/form-js-playground/src/components/PlaygroundRoot.js index 0b5065aef..a7e5b57e3 100644 --- a/packages/form-js-playground/src/components/PlaygroundRoot.js +++ b/packages/form-js-playground/src/components/PlaygroundRoot.js @@ -24,10 +24,15 @@ import './PlaygroundRoot.css'; export function PlaygroundRoot(props) { const { + actions: actionsConfig = {}, editor: editorConfig = {}, emit } = props; + const { + display: displayActions = true + } = actionsConfig; + const { inlinePropertiesPanel = true } = editorConfig; @@ -39,10 +44,12 @@ export function PlaygroundRoot(props) { const resultContainerRef = useRef(); const propertiesPanelContainerRef = useRef(); + const paletteRef = useRef(); const formEditorRef = useRef(); const formRef = useRef(); const dataEditorRef = useRef(); const resultViewRef = useRef(); + const propertiesPanelRef = useRef(); const [ showEmbed, setShowEmbed ] = useState(false); @@ -57,6 +64,12 @@ export function PlaygroundRoot(props) { // pipe to playground API useEffect(() => { props.onInit({ + attachDataContainer: (node) => dataEditorRef.current.attachTo(node), + attachEditorContainer: (node) => formEditorRef.current.attachTo(node), + attachFormContainer: (node) => formRef.current.attachTo(node), + attachPaletteContainer: (node) => paletteRef.current.attachTo(node), + attachPropertiesPanelContainer: (node) => propertiesPanelRef.current.attachTo(node), + attachResultContainer: (node) => resultViewRef.current.attachTo(node), get: (name, strict) => formEditorRef.current.get(name, strict), setSchema: setInitialSchema }); @@ -89,6 +102,9 @@ export function PlaygroundRoot(props) { } }); + paletteRef.current = formEditor.get('palette'); + propertiesPanelRef.current = formEditor.get('propertiesPanel'); + formEditor.on('changed', () => { setSchema(formEditor.getSchema()); }); @@ -178,19 +194,26 @@ export function PlaygroundRoot(props) {
- - - - - - + + { + displayActions && + + + } + + { + displayActions && + + + } +
diff --git a/packages/form-js-playground/test/spec/Playground.spec.js b/packages/form-js-playground/test/spec/Playground.spec.js index 4a5a067ea..6a25c7361 100644 --- a/packages/form-js-playground/test/spec/Playground.spec.js +++ b/packages/form-js-playground/test/spec/Playground.spec.js @@ -1,9 +1,13 @@ import 'preact/debug'; +import { forEach } from 'min-dash'; + import { act } from '@testing-library/preact/pure'; import { - query as domQuery + domify, + query as domQuery, + queryAll as domQueryAll } from 'min-dom'; import { @@ -39,6 +43,7 @@ describe('playground', function() { !singleStart && afterEach(function() { if (playground) { playground.destroy(); + playground = null; } }); @@ -90,6 +95,75 @@ describe('playground', function() { }); + it('should NOT attach to empty parent', async function() { + + // given + const data = { + creditor: 'foo' + }; + + // when + await act(() => { + new Playground({ + schema, + data + }); + }); + + const playgroundContainer = domQuery('.fjs-pgl-root', container); + + // then + expect(domQuery('.fjs-properties-panel', playgroundContainer)).to.not.exist; + }); + + + it('should render actions', async function() { + + // given + const data = { + creditor: 'foo' + }; + + // when + await act(() => { + playground = new Playground({ + container, + schema, + data + }); + }); + + const actions = domQueryAll('.fjs-pgl-button', container); + + // then + expect(actions.length).to.eql(2); + }); + + + it('should NOT render actions', async function() { + + // given + const data = { + creditor: 'foo' + }; + + // when + await act(() => { + playground = new Playground({ + container, + schema, + data, + actions: { display: false } + }); + }); + + const actions = domQueryAll('.fjs-pgl-button', container); + + // then + expect(actions.length).to.eql(0); + }); + + it('should render properties panel (inline)', async function() { // given @@ -184,6 +258,24 @@ describe('playground', function() { }); + it('#get', async function() { + + // given + await act(() => { + playground = new Playground({ + container, + schema + }); + }); + + // when + const eventBus = playground.get('eventBus'); + + // then + expect(eventBus).to.exist; + }); + + it('should emit ', async function() { // given @@ -207,4 +299,161 @@ describe('playground', function() { expect(spy).to.have.been.called; }); + + describe('attach components', function() { + + /** + * @typedef { { + * name: String, + * attachFn: String, + * selector: String + * } } PlaygroundComponent + */ + + /** @type Array */ + const components = [ + { + name: 'editor', + attachFn: 'attachEditorContainer', + selector: 'fjs-form-editor' + }, + { + name: 'preview', + attachFn: 'attachPreviewContainer', + selector: 'fjs-form' + }, + { + name: 'data', + attachFn: 'attachDataContainer', + selector: 'cm-editor' + }, + { + name: 'result', + attachFn: 'attachResultContainer', + selector: 'cm-editor' + }, + { + name: 'palette', + attachFn: 'attachPaletteContainer', + selector: 'fjs-palette-container' + }, + { + name: 'properties-panel', + attachFn: 'attachPropertiesPanelContainer', + selector: 'fjs-properties-panel' + }, + ]; + + forEach(components, ({ name, attachFn, selector }) => { + + describe(`attach ${name}`, function() { + + let parent; + + beforeEach(function() { + parent = domify(`
`); + document.body.appendChild(parent); + }); + + afterEach(function() { + document.body.removeChild(parent); + }); + + + it(`should throw when not initialized - ${name}`, async function() { + + // given + playground = new Playground({ + container, + schema + }); + + // then + expect(() => playground[attachFn](parent)).to.throw('Playground is not initialized.'); + }); + + + it(`should attach ${name}`, async function() { + + // given + await act(() => { + playground = new Playground({ + container, + schema + }); + }); + + // when + await act(() => { + playground[attachFn](parent); + }); + + const expectedContainer = domQuery(`.${selector}`, parent); + + // then + expect(expectedContainer).to.exist; + }); + + }); + + }); + + + describe('complex (attach alltogether)', function() { + + let testContainer; + + beforeEach(function() { + testContainer = document.createElement('div'); + document.body.appendChild(testContainer); + }); + + afterEach(function() { + document.body.removeChild(testContainer); + }); + + + it('should attach', async function() { + + // given + const withParent = components.map(component => { + const parent = domify(`
`); + testContainer.appendChild(parent); + return { + ...component, + parent + }; + }); + + const data = { + creditor: 'foo' + }; + + let playground; + await act(() => { + playground = new Playground({ + schema, + data, + editor: { inlinePropertiesPanel: false } + }); + }); + + // when + await act(() => { + forEach(withParent, ({ attachFn, parent }) => { + playground[attachFn](parent); + }); + }); + + // then + forEach(components, ({ selector, parent }) => { + expect(domQuery(`.${selector}`, parent)).to.exist; + }); + + }); + + }); + + }); + }); \ No newline at end of file