Skip to content

Commit

Permalink
test: provide custom form field tests
Browse files Browse the repository at this point in the history
Closes #123
  • Loading branch information
Niklas Kiefer committed Oct 5, 2023
1 parent 384dd76 commit c0c00e3
Show file tree
Hide file tree
Showing 11 changed files with 512 additions and 25 deletions.
34 changes: 34 additions & 0 deletions packages/form-js-playground/karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
const path = require('path');

const {
NormalModuleReplacementPlugin
} = require('webpack');

const coverage = process.env.COVERAGE;

// configures browsers to run test against
Expand Down Expand Up @@ -63,6 +69,10 @@ module.exports = function(karma) {
'css-loader'
]
},
{
test: /\.svg$/,
use: [ '@svgr/webpack' ]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
Expand Down Expand Up @@ -90,6 +100,30 @@ module.exports = function(karma) {
}
]
},
plugins: [
new NormalModuleReplacementPlugin(
/^(..\/preact|preact)(\/[^/]+)?$/,
function(resource) {

const replMap = {
'preact/hooks': path.resolve('../../node_modules/preact/hooks/dist/hooks.module.js'),
'preact/jsx-runtime': path.resolve('../../node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js'),
'preact': path.resolve('../../node_modules/preact/dist/preact.module.js'),
'../preact/hooks': path.resolve('../../node_modules/preact/hooks/dist/hooks.module.js'),
'../preact/jsx-runtime': path.resolve('../../node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js'),
'../preact': path.resolve('../../node_modules/preact/dist/preact.module.js')
};

const replacement = replMap[resource.request];

if (!replacement) {
return;
}

resource.request = replacement;
}
),
],
devtool: 'eval-source-map'
}
};
Expand Down
152 changes: 152 additions & 0 deletions packages/form-js-playground/test/custom/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { get, set } from 'min-dash';

import {
NumberFieldEntry,
isNumberFieldEntryEdited
} from '@bpmn-io/properties-panel';


class CustomPropertiesProvider {
constructor(propertiesPanel) {
propertiesPanel.registerProvider(this, 500);
}

getGroups(field, editField) {
return (groups) => {

if (field.type !== 'range') {
return groups;
}

const generalIdx = findGroupIdx(groups, 'general');

groups.splice(generalIdx + 1, 0, {
id: 'range',
label: 'Range',
entries: RangeEntries(field, editField)
});

return groups;
};
}
}

CustomPropertiesProvider.$inject = [ 'propertiesPanel' ];

function RangeEntries(field, editField) {

const onChange = (key) => {
return (value) => {
const range = get(field, [ 'range' ], {});

editField(field, [ 'range' ], set(range, [ key ], value));
};
};

const getValue = (key) => {
return () => {
return get(field, [ 'range', key ]);
};
};

return [
{
id: 'range-min',
component: Min,
getValue,
field,
isEdited: isNumberFieldEntryEdited,
onChange
},
{
id: 'range-max',
component: Max,
getValue,
field,
isEdited: isNumberFieldEntryEdited,
onChange
},
{
id: 'range-step',
component: Step,
getValue,
field,
isEdited: isNumberFieldEntryEdited,
onChange
}
];

}

function Min(props) {
const {
field,
getValue,
id,
onChange
} = props;

const debounce = (fn) => fn;

return NumberFieldEntry({
debounce,
element: field,
getValue: getValue('min'),
id,
label: 'Minimum',
setValue: onChange('min')
});
}

function Max(props) {
const {
field,
getValue,
id,
onChange
} = props;

const debounce = (fn) => fn;

return NumberFieldEntry({
debounce,
element: field,
getValue: getValue('max'),
id,
label: 'Maximum',
setValue: onChange('max')
});
}

function Step(props) {
const {
field,
getValue,
id,
onChange
} = props;

const debounce = (fn) => fn;

return NumberFieldEntry({
debounce,
element: field,
getValue: getValue('step'),
id,
min: 0,
label: 'Step',
setValue: onChange('step')
});
}


export default {
__init__: [ 'customPropertiesProvider' ],
customPropertiesProvider: [ 'type', CustomPropertiesProvider ]
};

// helper //////////////////////

function findGroupIdx(groups, id) {
return groups.findIndex(g => g.id === id);
}
19 changes: 19 additions & 0 deletions packages/form-js-playground/test/custom/range.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/form-js-playground/test/custom/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.range-group {
display: flex;
flex-direction: row;
}
122 changes: 122 additions & 0 deletions packages/form-js-playground/test/custom/viewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

import {
Errors,
FormContext,
Numberfield,
Description,
Label
} from '@bpmn-io/form-js-viewer';

import { useContext } from 'preact/hooks';

import classNames from 'classnames';

import RangeIcon from './range.svg';

const rangeType = 'range';

function RangeRenderer(props) {

const {
disabled,
errors = [],
field,
readonly,
value
} = props;

const {
description,
range = {},
id,
label
} = field;

const {
min,
max,
step
} = range;

const { formId } = useContext(FormContext);

const errorMessageId = errors.length === 0 ? undefined : `${prefixId(id, formId)}-error-message`;

const onChange = ({ target }) => {
props.onChange({
field,
value: Number(target.value)
});
};

return <div class={ formFieldClasses(rangeType) }>
<Label
id={ prefixId(id, formId) }
label={ label } />
<div class="range-group">
<input
type="range"
disabled={ disabled }
id={ prefixId(id, formId) }
max={ max }
min={ min }
onInput={ onChange }
readOnly={ readonly }
value={ value }
step={ step } />
<div class="range-value">{ value }</div>
</div>
<Description description={ description } />
<Errors errors={ errors } id={ errorMessageId } />
</div>;
}

RangeRenderer.config = {
...Numberfield.config,
type: rangeType,
keyed: true,
label: 'Range',
group: 'basic-input',
propertiesPanelEntries: [
'key',
'label',
'description',
'min',
'max'
],
icon: RangeIcon
};

class CustomFormFields {
constructor(formFields) {
formFields.register(rangeType, RangeRenderer);
}
}

export default {
__init__: [ 'customFormFields' ],
customFormFields: [ 'type', CustomFormFields ]
};


// helper //////////////////////

function formFieldClasses(type, { errors = [], disabled = false, readonly = false } = {}) {
if (!type) {
throw new Error('type required');
}

return classNames('fjs-form-field', `fjs-form-field-${type}`, {
'fjs-has-errors': errors.length > 0,
'fjs-disabled': disabled,
'fjs-readonly': readonly
});
}

function prefixId(id, formId) {
if (formId) {
return `fjs-form-${ formId }-${ id }`;
}

return `fjs-form-${ id }`;
}
Loading

0 comments on commit c0c00e3

Please sign in to comment.