From 8768af1732faa020d8e0be391c98fc04ccde4f57 Mon Sep 17 00:00:00 2001 From: Rajeev K Tomy Date: Sun, 20 Oct 2024 17:17:37 +0200 Subject: [PATCH] #375 Make country renderer and region renderer as custom field renderer components --- .../src/fieldRenderers/FieldRenderer.js | 49 +++++++++++++++ .../countryRenderer}/CountryRenderer.jsx | 4 +- .../components/countryRenderer/condition.js | 3 + .../components/countryRenderer/index.js | 4 ++ .../src/fieldRenderers/customRenderers.js | 31 ++++++++++ .../regionRenderer}/RegionRenderer.jsx | 9 ++- .../regionRenderer/condition.js | 3 + .../fieldRenderers/regionRenderer/index.js | 5 ++ src/reactapp/src/fieldRenderers/renderers.js | 61 +++---------------- 9 files changed, 113 insertions(+), 56 deletions(-) create mode 100644 src/reactapp/src/fieldRenderers/FieldRenderer.js rename src/reactapp/src/{components/address/components => fieldRenderers/components/countryRenderer}/CountryRenderer.jsx (87%) create mode 100644 src/reactapp/src/fieldRenderers/components/countryRenderer/condition.js create mode 100644 src/reactapp/src/fieldRenderers/components/countryRenderer/index.js create mode 100644 src/reactapp/src/fieldRenderers/customRenderers.js rename src/reactapp/src/{components/address/components => fieldRenderers/regionRenderer}/RegionRenderer.jsx (72%) create mode 100644 src/reactapp/src/fieldRenderers/regionRenderer/condition.js create mode 100644 src/reactapp/src/fieldRenderers/regionRenderer/index.js diff --git a/src/reactapp/src/fieldRenderers/FieldRenderer.js b/src/reactapp/src/fieldRenderers/FieldRenderer.js new file mode 100644 index 00000000..9651a78a --- /dev/null +++ b/src/reactapp/src/fieldRenderers/FieldRenderer.js @@ -0,0 +1,49 @@ +import { + ConfigMultiline, + ConfigTextInput, + ConfigSelectInput, +} from '../components/common/Form'; +import { _emptyFunc } from '../utils'; +import { FieldType } from '../utils/field'; + +export class FieldRenderer { + rendererList = []; + + constructor() { + this.#registerDefaultRenderers(); + } + + register(renderer, sortOrder = 100, conditionCallback = _emptyFunc()) { + const newRenderer = { + renderer, + sortOrder, + canRenderField: conditionCallback, + }; + this.rendererList = [...this.rendererList, newRenderer].sort( + (renderer1, renderer2) => renderer1.sortOrder - renderer2.sortOrder + ); + } + + getRendererForField(field) { + // eslint-disable-next-line no-restricted-syntax + for (const renderer of this.rendererList) { + if (renderer.canRenderField(field)) { + return renderer.renderer; + } + } + + return false; + } + + #registerDefaultRenderers() { + this.register(ConfigTextInput, 350, (field) => + FieldType.isText(field.type) + ); + this.register(ConfigSelectInput, 400, (field) => + FieldType.isSelect(field.type) + ); + this.register(ConfigMultiline, 300, (field) => + FieldType.isMultiline(field.type) + ); + } +} diff --git a/src/reactapp/src/components/address/components/CountryRenderer.jsx b/src/reactapp/src/fieldRenderers/components/countryRenderer/CountryRenderer.jsx similarity index 87% rename from src/reactapp/src/components/address/components/CountryRenderer.jsx rename to src/reactapp/src/fieldRenderers/components/countryRenderer/CountryRenderer.jsx index c55d0ac2..a8c4cde9 100644 --- a/src/reactapp/src/components/address/components/CountryRenderer.jsx +++ b/src/reactapp/src/fieldRenderers/components/countryRenderer/CountryRenderer.jsx @@ -1,8 +1,8 @@ import React from 'react'; -import { ConfigSelectInput } from '../../common/Form'; -import { prepareCountryOptions } from '../utility'; +import { ConfigSelectInput } from '../../../components/common/Form'; import useAppContext from '../../../hook/useAppContext'; +import { prepareCountryOptions } from '../../../components/address/utility'; import { fieldConfigShape, formikDataShape } from '../../../utils/propTypes'; function CountryRenderer({ formikData, config }) { diff --git a/src/reactapp/src/fieldRenderers/components/countryRenderer/condition.js b/src/reactapp/src/fieldRenderers/components/countryRenderer/condition.js new file mode 100644 index 00000000..6a1a86d5 --- /dev/null +++ b/src/reactapp/src/fieldRenderers/components/countryRenderer/condition.js @@ -0,0 +1,3 @@ +export default function countryRendererCondition(field) { + return field.code === 'country_id'; +} diff --git a/src/reactapp/src/fieldRenderers/components/countryRenderer/index.js b/src/reactapp/src/fieldRenderers/components/countryRenderer/index.js new file mode 100644 index 00000000..7772ad07 --- /dev/null +++ b/src/reactapp/src/fieldRenderers/components/countryRenderer/index.js @@ -0,0 +1,4 @@ +import CountryRenderer from './CountryRenderer'; + +export default CountryRenderer; +export { default as countryRendererCondition } from './condition'; diff --git a/src/reactapp/src/fieldRenderers/customRenderers.js b/src/reactapp/src/fieldRenderers/customRenderers.js new file mode 100644 index 00000000..0b551bb4 --- /dev/null +++ b/src/reactapp/src/fieldRenderers/customRenderers.js @@ -0,0 +1,31 @@ +import CountryRenderer, { + countryRendererCondition, +} from './components/countryRenderer'; +import RegionRenderer, { regionRendererCondition } from './regionRenderer'; + +/** + * Holds custom renderers. + * + * renderer: React component that will be used to render the field. + * + * sortOrder: This defines the priority of the renderer component. All registered + * components have a sort order associated with it. When a field looks for + * a renderer, it loops through all the registered renderers and find a + * suitable renderer that can render the field based on the "condition". + * Lower the sort order, higher the chance to use the renderer. + * + * condition: This is a callback which determines whether the renderer can be + * used to render the field. + */ +export default [ + { + renderer: CountryRenderer, + sortOrder: 100, + condition: countryRendererCondition, + }, + { + renderer: RegionRenderer, + sortOrder: 200, + condition: regionRendererCondition, + }, +]; diff --git a/src/reactapp/src/components/address/components/RegionRenderer.jsx b/src/reactapp/src/fieldRenderers/regionRenderer/RegionRenderer.jsx similarity index 72% rename from src/reactapp/src/components/address/components/RegionRenderer.jsx rename to src/reactapp/src/fieldRenderers/regionRenderer/RegionRenderer.jsx index 1bb50f09..102fce8a 100644 --- a/src/reactapp/src/components/address/components/RegionRenderer.jsx +++ b/src/reactapp/src/fieldRenderers/regionRenderer/RegionRenderer.jsx @@ -1,8 +1,11 @@ import React from 'react'; -import { ConfigSelectInput, ConfigTextInput } from '../../common/Form'; -import useCountryState from '../hooks/useCountryState'; -import { fieldConfigShape, formikDataShape } from '../../../utils/propTypes'; +import { + ConfigTextInput, + ConfigSelectInput, +} from '../../components/common/Form'; +import { fieldConfigShape, formikDataShape } from '../../utils/propTypes'; +import useCountryState from '../../components/address/hooks/useCountryState'; function RegionRenderer({ formikData, config }) { const { stateOptions, hasStateOptions } = useCountryState({ diff --git a/src/reactapp/src/fieldRenderers/regionRenderer/condition.js b/src/reactapp/src/fieldRenderers/regionRenderer/condition.js new file mode 100644 index 00000000..418a261b --- /dev/null +++ b/src/reactapp/src/fieldRenderers/regionRenderer/condition.js @@ -0,0 +1,3 @@ +export default function regionRendererCondition(field) { + return field.code === 'region'; +} diff --git a/src/reactapp/src/fieldRenderers/regionRenderer/index.js b/src/reactapp/src/fieldRenderers/regionRenderer/index.js new file mode 100644 index 00000000..83798c75 --- /dev/null +++ b/src/reactapp/src/fieldRenderers/regionRenderer/index.js @@ -0,0 +1,5 @@ +import RegionRenderer from './RegionRenderer'; + +export default RegionRenderer; + +export { default as regionRendererCondition } from './condition'; diff --git a/src/reactapp/src/fieldRenderers/renderers.js b/src/reactapp/src/fieldRenderers/renderers.js index f438c3d1..a734f25e 100644 --- a/src/reactapp/src/fieldRenderers/renderers.js +++ b/src/reactapp/src/fieldRenderers/renderers.js @@ -1,53 +1,12 @@ -import { - ConfigMultiline, - ConfigTextInput, - ConfigSelectInput, -} from '../components/common/Form'; -import { _emptyFunc } from '../utils'; -import { FieldType } from '../utils/field'; -import RegionRenderer from '../components/address/components/RegionRenderer'; -import CountryRenderer from '../components/address/components/CountryRenderer'; - -class FieldRenderer { - rendererList = []; - - constructor() { - this.#registerDefaultRenderers(); - } - - register(renderer, sortOrder = 100, conditionCallback = _emptyFunc()) { - const newRenderer = { - renderer, - sortOrder, - canRenderField: conditionCallback, - }; - this.rendererList = [...this.rendererList, newRenderer].sort( - (renderer1, renderer2) => renderer1.sortOrder - renderer2.sortOrder - ); - } - - getRendererForField(field) { - // eslint-disable-next-line no-restricted-syntax - for (const renderer of this.rendererList) { - if (renderer.canRenderField(field)) { - return renderer.renderer; - } - } - - return false; - } - - #registerDefaultRenderers() { - this.register(CountryRenderer, 10, (field) => field.code === 'country_id'); - this.register(RegionRenderer, 20, (field) => field.code === 'region'); - this.register(ConfigTextInput, 30, (field) => FieldType.isText(field.type)); - this.register(ConfigSelectInput, 10, (field) => - FieldType.isSelect(field.type) - ); - this.register(ConfigMultiline, 10, (field) => - FieldType.isMultiline(field.type) - ); - } -} +import customRenderers from './customRenderers'; +import { FieldRenderer } from './FieldRenderer'; export const addressFieldRenderer = new FieldRenderer(); + +/** + * Here adds custom field renderers for address form. + */ +customRenderers.forEach((customrenderer) => { + const { renderer, sortOrder, condition } = customrenderer; + addressFieldRenderer.register(renderer, sortOrder, condition); +});