From fd61ac36bfbb5e2d0e6442bcc7655e773f66660d Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Fri, 2 Sep 2022 14:47:49 +0200 Subject: [PATCH] wip overrides context Related to https://github.com/bpmn-io/bpmn-js-properties-panel/issues/737 --- src/PropertiesPanel.js | 113 +++++++++++++++++----------- src/components/entries/Select.js | 16 +++- src/components/entries/TextField.js | 25 ++++-- src/context/OverridesContext.js | 10 +++ src/context/index.js | 1 + src/hooks/index.js | 1 + src/hooks/useOverridesContext.js | 29 +++++++ 7 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 src/context/OverridesContext.js create mode 100644 src/hooks/useOverridesContext.js diff --git a/src/PropertiesPanel.js b/src/PropertiesPanel.js index fbfda1da..a7208183 100644 --- a/src/PropertiesPanel.js +++ b/src/PropertiesPanel.js @@ -23,6 +23,7 @@ import { ErrorsContext, EventContext, LayoutContext, + OverridesContext, PropertiesPanelContext } from './context'; @@ -37,6 +38,8 @@ const DEFAULT_LAYOUT = { const DEFAULT_DESCRIPTION = {}; +const DEFAULT_OVERRIDES = {}; + const bufferedEvents = [ 'propertiesPanel.showEntry', 'propertiesPanel.setErrors' @@ -101,27 +104,29 @@ const bufferedEvents = [ * data from implementor to describe *what* will be rendered. * * @param {Object} props + * @param {Function} [props.descriptionLoaded] + * @param {DescriptionConfig} [props.descriptionConfig] + * @param {Object} [props.eventBus] * @param {Object|Array} props.element - * @param {import('./components/Header').HeaderProvider} props.headerProvider - * @param {PlaceholderProvider} [props.placeholderProvider] * @param {Array} props.groups - * @param {Object} [props.layoutConfig] + * @param {import('./components/Header').HeaderProvider} props.headerProvider * @param {Function} [props.layoutChanged] - * @param {DescriptionConfig} [props.descriptionConfig] - * @param {Function} [props.descriptionLoaded] - * @param {Object} [props.eventBus] + * @param {Object} [props.layoutConfig] + * @param {Object} [props.overridesConfig] + * @param {PlaceholderProvider} [props.placeholderProvider] */ export default function PropertiesPanel(props) { const { + descriptionConfig = {}, + descriptionLoaded, element, - headerProvider, - placeholderProvider, + eventBus, groups, - layoutConfig = {}, + headerProvider, layoutChanged, - descriptionConfig = {}, - descriptionLoaded, - eventBus + layoutConfig = {}, + overridesConfig = {}, + placeholderProvider } = props; // set-up layout context @@ -166,6 +171,19 @@ export default function PropertiesPanel(props) { getDescriptionForId }; + // set-up overrides context + const overrides = createOverridesContext(overridesConfig); + + const getOverridesForId = (id) => { + return overrides[id] || {}; + }; + + const overridesContext = { + overrides, + getOverridesForId + }; + + // set-up errors context useEventBuffer(bufferedEvents, eventBus); const [ errors, setErrors ] = useState({}); @@ -199,38 +217,40 @@ export default function PropertiesPanel(props) { return ( - - - -
-
-
- { - groups.map(group => { - const { - component: Component = Group, - id - } = group; - - return ( - - ); - }) - } + + + + +
+
+
+ { + groups.map(group => { + const { + component: Component = Group, + id + } = group; + + return ( + + ); + }) + } +
-
- - - + + + + ); @@ -251,4 +271,11 @@ function createDescriptionContext(overrides) { ...DEFAULT_DESCRIPTION, ...overrides }; +} + +function createOverridesContext(overrides) { + return { + ...DEFAULT_OVERRIDES, + ...overrides + }; } \ No newline at end of file diff --git a/src/components/entries/Select.js b/src/components/entries/Select.js index 772577eb..9a49e1bf 100644 --- a/src/components/entries/Select.js +++ b/src/components/entries/Select.js @@ -1,7 +1,10 @@ import classNames from 'classnames'; +import { isFunction } from 'min-dash'; + import { useError, + useOverridesContext, useShowEntryEvent } from '../../hooks'; @@ -111,8 +114,15 @@ export default function SelectEntry(props) { disabled } = props; - const value = getValue(element); - const options = getOptions(element); + const { + getValue: overrideGetValue, + setValue: overrideSetValue, + getOptions: overrideGetOptions + } = useOverridesContext(id); + + const value = isFunction(overrideGetValue) ? overrideGetValue(element) : getValue(element); + const options = isFunction(overrideGetOptions) ? overrideGetOptions(element) : getOptions(element); + const onChange = isFunction(overrideSetValue) ? overrideSetValue : setValue; const error = useError(id); @@ -128,7 +138,7 @@ export default function SelectEntry(props) { key={ element } label={ label } value={ value } - onChange={ setValue } + onChange={ onChange } options={ options } disabled={ disabled } /> { error &&
{ error }
} diff --git a/src/components/entries/TextField.js b/src/components/entries/TextField.js index dfe247eb..94fef995 100644 --- a/src/components/entries/TextField.js +++ b/src/components/entries/TextField.js @@ -12,6 +12,7 @@ import { isFunction } from 'min-dash'; import { useError, + useOverridesContext, usePrevious, useShowEntryEvent } from '../../hooks'; @@ -99,29 +100,41 @@ export default function TextfieldEntry(props) { const globalError = useError(id); const [ localError, setLocalError ] = useState(null); - let value = getValue(element); + const { + getValue: overrideGetValue, + setValue: overrideSetValue, + validate: overrideValidate + } = useOverridesContext(id); + + let value = isFunction(overrideGetValue) ? overrideGetValue(element) : getValue(element); const previousValue = usePrevious(value); useEffect(() => { - if (isFunction(validate)) { - const newValidationError = validate(value) || null; + let newValidationError; - setLocalError(newValidationError); + if (isFunction(overrideValidate)) { + newValidationError = overrideValidate(value) || null; + } else if (isFunction(validate)) { + newValidationError = validate(value) || null; } + + newValidationError && setLocalError(newValidationError); }, [ value ]); const onInput = (newValue) => { let newValidationError = null; - if (isFunction(validate)) { + if (isFunction(overrideValidate)) { + newValidationError = overrideValidate(newValue) || null; + } else if (isFunction(validate)) { newValidationError = validate(newValue) || null; } if (newValidationError) { setCachedInvalidValue(newValue); } else { - setValue(newValue); + isFunction(overrideSetValue) ? overrideSetValue(newValue) : setValue(newValue); } setLocalError(newValidationError); diff --git a/src/context/OverridesContext.js b/src/context/OverridesContext.js new file mode 100644 index 00000000..878240ec --- /dev/null +++ b/src/context/OverridesContext.js @@ -0,0 +1,10 @@ +import { + createContext +} from 'preact'; + +const OverridesContext = createContext({ + overrides: {}, + getOverridesForId: () => {} +}); + +export default OverridesContext; diff --git a/src/context/index.js b/src/context/index.js index 27ddc1e5..4d48e838 100644 --- a/src/context/index.js +++ b/src/context/index.js @@ -2,4 +2,5 @@ export { default as DescriptionContext } from './DescriptionContext'; export { default as ErrorsContext } from './ErrorsContext'; export { default as EventContext } from './EventContext'; export { default as LayoutContext } from './LayoutContext'; +export { default as OverridesContext } from './OverridesContext'; export { default as PropertiesPanelContext } from './LayoutContext'; diff --git a/src/hooks/index.js b/src/hooks/index.js index 3ee1a406..d706a3d2 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -4,6 +4,7 @@ export { useEvent } from './useEvent'; export { useEventBuffer } from './useEventBuffer'; export { useKeyFactory } from './useKeyFactory'; export { useLayoutState } from './useLayoutState'; +export { useOverridesContext } from './useOverridesContext'; export { usePrevious } from './usePrevious'; export { useShowEntryEvent } from './useShowEntryEvent'; export { useStickyIntersectionObserver } from './useStickyIntersectionObserver'; diff --git a/src/hooks/useOverridesContext.js b/src/hooks/useOverridesContext.js new file mode 100644 index 00000000..40cd271e --- /dev/null +++ b/src/hooks/useOverridesContext.js @@ -0,0 +1,29 @@ +import { + useContext +} from 'preact/hooks'; + +import { + OverridesContext +} from '../context'; + +/** + * Accesses the global OverridesContext and returns custom handlers for a given id and element. + * + * @example + * ```jsx + * function TextField(props) { + * const { getValue } = OverridesContext('input1'); + * } + * ``` + * + * @param {string} id + * + * @returns {string} + */ +export function useOverridesContext(id) { + const { + getOverridesForId + } = useContext(OverridesContext); + + return getOverridesForId(id); +}