From 23be348cd8bb6b2adcec3328e52dcb256a50f71a Mon Sep 17 00:00:00 2001 From: Valentin Serra Date: Wed, 18 Oct 2023 10:39:42 +0200 Subject: [PATCH] feat: implemented events Closes #785 --- packages/form-js-viewer/README.md | 3 ++- .../render/components/form-fields/Taglist.js | 16 +++++++++------- .../form-fields/parts/SearchableSelect.js | 19 +++++++++++-------- .../components/form-fields/Select.spec.js | 9 ++++++++- .../components/form-fields/Taglist.spec.js | 10 ++++++++++ .../components/form-fields/helper/index.js | 7 ++++++- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/packages/form-js-viewer/README.md b/packages/form-js-viewer/README.md index 8600ed4da..d740a73a2 100644 --- a/packages/form-js-viewer/README.md +++ b/packages/form-js-viewer/README.md @@ -180,7 +180,8 @@ Fired whenever a schema has finished importing, whether it succeeds or fails. - `formField.add :: { formField }` - `formField.remove :: { formField }` - `formField.focus :: { formField }` -- `formField.blur :: { formField }` +- `formField.blur :: { formField }` +- `formField.search :: { formField, value }` ## License diff --git a/packages/form-js-viewer/src/render/components/form-fields/Taglist.js b/packages/form-js-viewer/src/render/components/form-fields/Taglist.js index 19911c52b..dc1f92c8a 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/Taglist.js +++ b/packages/form-js-viewer/src/render/components/form-fields/Taglist.js @@ -1,6 +1,7 @@ import { useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks'; import useValuesAsync, { LOAD_STATES } from '../../hooks/useValuesAsync'; +import { useService } from '../../hooks'; import { FormContext } from '../../context'; import classNames from 'classnames'; @@ -14,7 +15,6 @@ import Label from '../Label'; import SkipLink from './parts/SkipLink'; import { sanitizeMultiSelectValue } from '../util/sanitizerUtil'; - import { createEmptyOptions } from '../util/valuesUtil'; import { @@ -53,6 +53,7 @@ export default function Taglist(props) { const [ isEscapeClosed, setIsEscapeClose ] = useState(false); const focusScopeRef = useRef(); const inputRef = useRef(); + const eventBus = useService('eventBus'); const { state: loadState, @@ -76,11 +77,6 @@ export default function Taglist(props) { setHasOptionsLeft(options.length > values.length); }, [ options.length, values.length ]); - const onFilterChange = ({ target }) => { - setIsEscapeClose(false); - setFilter(target.value); - }; - const selectValue = (value) => { if (filter) { setFilter(''); @@ -98,6 +94,12 @@ export default function Taglist(props) { props.onChange({ value: values.filter((v) => v != value), field }); }; + const onInputChange = ({ target }) => { + setIsEscapeClose(false); + setFilter(target.value || ''); + eventBus.fire('formField.search', { formField: field, value: target.value || '' }); + }; + const onInputKeyDown = (e) => { switch (e.key) { @@ -215,7 +217,7 @@ export default function Taglist(props) { class="fjs-taglist-input" ref={ inputRef } id={ prefixId(`${id}-search`, formId) } - onChange={ onFilterChange } + onChange={ onInputChange } type="text" value={ filter } placeholder={ (disabled || readonly) ? undefined : 'Search' } diff --git a/packages/form-js-viewer/src/render/components/form-fields/parts/SearchableSelect.js b/packages/form-js-viewer/src/render/components/form-fields/parts/SearchableSelect.js index 9cc5528e2..24da67741 100644 --- a/packages/form-js-viewer/src/render/components/form-fields/parts/SearchableSelect.js +++ b/packages/form-js-viewer/src/render/components/form-fields/parts/SearchableSelect.js @@ -1,5 +1,6 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks'; import useValuesAsync, { LOAD_STATES } from '../../../hooks/useValuesAsync'; +import { useService } from '../../../hooks'; import { FormContext } from '../../../context'; @@ -33,6 +34,7 @@ export default function SearchableSelect(props) { const [ shouldApplyFilter, setShouldApplyFilter ] = useState(true); const [ isEscapeClosed, setIsEscapeClose ] = useState(false); const searchbarRef = useRef(); + const eventBus = useService('eventBus'); const { state: loadState, @@ -57,13 +59,6 @@ export default function SearchableSelect(props) { }, [ filter, loadState, options, shouldApplyFilter ]); - const onChange = ({ target }) => { - setIsEscapeClose(false); - setIsDropdownExpanded(true); - setShouldApplyFilter(true); - setFilter(target.value || ''); - }; - const setValue = useCallback((option) => { setFilter(option && option.label || ''); props.onChange({ value: option && option.value || null, field }); @@ -88,6 +83,14 @@ export default function SearchableSelect(props) { }, [ isDropdownExpanded ]); + const onInputChange = ({ target }) => { + setIsEscapeClose(false); + setIsDropdownExpanded(true); + setShouldApplyFilter(true); + setFilter(target.value || ''); + eventBus.fire('formField.search', { formField: field, value: target.value || '' }); + }; + const onInputKeyDown = useCallback((keyDownEvent) => { switch (keyDownEvent.key) { @@ -141,7 +144,7 @@ export default function SearchableSelect(props) { class="fjs-input" ref={ searchbarRef } id={ prefixId(`${id}-search`, formId) } - onChange={ onChange } + onChange={ onInputChange } type="text" value={ filter } placeholder={ 'Search' } diff --git a/packages/form-js-viewer/test/spec/render/components/form-fields/Select.spec.js b/packages/form-js-viewer/test/spec/render/components/form-fields/Select.spec.js index cb9bd1889..746f169df 100644 --- a/packages/form-js-viewer/test/spec/render/components/form-fields/Select.spec.js +++ b/packages/form-js-viewer/test/spec/render/components/form-fields/Select.spec.js @@ -768,7 +768,9 @@ describe('Select', function() { it('should filter dropdown', function() { // when - const { container } = createSelect({ field: { ...defaultField, searchable: true } }); + const eventBusFireSpy = spy(); + const field = { ...defaultField, searchable: true }; + const { container } = createSelect({ field, eventBusFire: eventBusFireSpy }); const filterInput = container.querySelector('input[type="text"]'); fireEvent.focus(filterInput); @@ -785,6 +787,11 @@ describe('Select', function() { expect(listItems.length).to.equal(1); expect(listItems[0].innerText).to.equal('German'); + expect(eventBusFireSpy).to.have.been.calledWith('formField.search', { + formField: field, + value: 'Ger' + }); + }); diff --git a/packages/form-js-viewer/test/spec/render/components/form-fields/Taglist.spec.js b/packages/form-js-viewer/test/spec/render/components/form-fields/Taglist.spec.js index f09222620..4ed974029 100644 --- a/packages/form-js-viewer/test/spec/render/components/form-fields/Taglist.spec.js +++ b/packages/form-js-viewer/test/spec/render/components/form-fields/Taglist.spec.js @@ -424,8 +424,10 @@ describe('Taglist', function() { it('should filter dropdown', function() { // given + const eventBusFireSpy = spy(); const { container } = createTaglist({ onChange: () => {}, + eventBusFire: eventBusFireSpy, value: [ 'tag1', 'tag2', 'tag3' ] }); @@ -440,9 +442,17 @@ describe('Taglist', function() { // then fireEvent.input(filterInput, { target: { value: '4' } }); expect(dropdownList.children.length).to.equal(1); + expect(eventBusFireSpy).to.have.been.calledWith('formField.search', { + formField: defaultField, + value: '4' + }); fireEvent.input(filterInput, { target: { value: 'Tag' } }); expect(dropdownList.children.length).to.equal(8); + expect(eventBusFireSpy).to.have.been.calledWith('formField.search', { + formField: defaultField, + value: 'Tag' + }); }); diff --git a/packages/form-js-viewer/test/spec/render/components/form-fields/helper/index.js b/packages/form-js-viewer/test/spec/render/components/form-fields/helper/index.js index e29280f27..bd2621ea3 100644 --- a/packages/form-js-viewer/test/spec/render/components/form-fields/helper/index.js +++ b/packages/form-js-viewer/test/spec/render/components/form-fields/helper/index.js @@ -19,11 +19,16 @@ export function WithFormContext(Component, options = {}, formId = 'foo') { formFields = new FormFields(), children = [], updateFieldValidation, + eventBusFire = () => {}, properties = {}, initialData } = options; - if (type === 'form') { + if (type === 'eventBus') { + return { + fire: eventBusFire + }; + } else if (type === 'form') { return { _getState() { return {