diff --git a/packages/core/src/i18n/combinatorTranslations.ts b/packages/core/src/i18n/combinatorTranslations.ts new file mode 100644 index 000000000..0b45c1512 --- /dev/null +++ b/packages/core/src/i18n/combinatorTranslations.ts @@ -0,0 +1,28 @@ +export interface CombinatorDefaultTranslation { + key: CombinatorTranslationEnum; + default: (variable?: string) => string; +} + +export enum CombinatorTranslationEnum { + clearDialogTitle = 'clearDialogTitle', + clearDialogMessage = 'clearDialogMessage', + clearDialogAccept = 'clearDialogAccept', + clearDialogDecline = 'clearDialogDecline', +} + +export type CombinatorTranslations = { + [key in CombinatorTranslationEnum]?: string; +}; + +export const combinatorDefaultTranslations: CombinatorDefaultTranslation[] = [ + { + key: CombinatorTranslationEnum.clearDialogTitle, + default: () => 'Clear form?', + }, + { + key: CombinatorTranslationEnum.clearDialogMessage, + default: () => 'Your data will be cleared. Do you want to proceed?', + }, + { key: CombinatorTranslationEnum.clearDialogAccept, default: () => 'Yes' }, + { key: CombinatorTranslationEnum.clearDialogDecline, default: () => 'No' }, +]; diff --git a/packages/core/src/i18n/i18nUtil.ts b/packages/core/src/i18n/i18nUtil.ts index 26ba88941..3d398f5f9 100644 --- a/packages/core/src/i18n/i18nUtil.ts +++ b/packages/core/src/i18n/i18nUtil.ts @@ -7,6 +7,10 @@ import { ArrayDefaultTranslation, ArrayTranslations, } from './arrayTranslations'; +import { + CombinatorDefaultTranslation, + CombinatorTranslations, +} from './combinatorTranslations'; export const getI18nKeyPrefixBySchema = ( schema: i18nJsonSchema | undefined, @@ -173,3 +177,17 @@ export const getArrayTranslations = ( }); return translations; }; + +export const getCombinatorTranslations = ( + t: Translator, + defaultTranslations: CombinatorDefaultTranslation[], + i18nKeyPrefix: string, + label: string +): CombinatorTranslations => { + const translations: CombinatorTranslations = {}; + defaultTranslations.forEach((controlElement) => { + const key = addI18nKeyToPrefix(i18nKeyPrefix, controlElement.key); + translations[controlElement.key] = t(key, controlElement.default(label)); + }); + return translations; +}; diff --git a/packages/core/src/i18n/index.ts b/packages/core/src/i18n/index.ts index 987aa3611..f5fc7de52 100644 --- a/packages/core/src/i18n/index.ts +++ b/packages/core/src/i18n/index.ts @@ -1,3 +1,4 @@ export * from './i18nTypes'; export * from './i18nUtil'; export * from './arrayTranslations'; +export * from './combinatorTranslations'; diff --git a/packages/core/src/util/renderer.ts b/packages/core/src/util/renderer.ts index 6aba2bdcb..93b47c91d 100644 --- a/packages/core/src/util/renderer.ts +++ b/packages/core/src/util/renderer.ts @@ -70,6 +70,9 @@ import { getI18nKeyPrefixBySchema, getArrayTranslations, Translator, + CombinatorTranslations, + getCombinatorTranslations, + combinatorDefaultTranslations, } from '../i18n'; import { arrayDefaultTranslations, @@ -970,6 +973,7 @@ export interface StatePropsOfCombinator extends StatePropsOfControl { indexOfFittingSchema: number; uischemas: JsonFormsUISchemaRegistryEntry[]; data: any; + translations: CombinatorTranslations; } export const mapStateToCombinatorRendererProps = ( @@ -977,12 +981,17 @@ export const mapStateToCombinatorRendererProps = ( ownProps: OwnPropsOfControl, keyword: CombinatorKeyword ): StatePropsOfCombinator => { - const { data, schema, rootSchema, ...props } = mapStateToControlProps( - state, - ownProps - ); + const { data, schema, rootSchema, i18nKeyPrefix, label, ...props } = + mapStateToControlProps(state, ownProps); const ajv = state.jsonforms.core.ajv; + const t = getTranslator()(state); + const translations = getCombinatorTranslations( + t, + combinatorDefaultTranslations, + i18nKeyPrefix, + label + ); const structuralKeywords = [ 'required', 'additionalProperties', @@ -1025,8 +1034,11 @@ export const mapStateToCombinatorRendererProps = ( schema, rootSchema, ...props, + i18nKeyPrefix, + label, indexOfFittingSchema, uischemas: getUISchemas(state), + translations, }; }; diff --git a/packages/vue/vue-vanilla/src/complex/ObjectRenderer.vue b/packages/vue/vue-vanilla/src/complex/ObjectRenderer.vue new file mode 100644 index 000000000..fdf0f07eb --- /dev/null +++ b/packages/vue/vue-vanilla/src/complex/ObjectRenderer.vue @@ -0,0 +1,84 @@ + + + + + + + diff --git a/packages/vue/vue-vanilla/src/complex/OneOfRenderer.vue b/packages/vue/vue-vanilla/src/complex/OneOfRenderer.vue new file mode 100644 index 000000000..f068f5856 --- /dev/null +++ b/packages/vue/vue-vanilla/src/complex/OneOfRenderer.vue @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + {{ control.translations.clearDialogTitle }} + + + + {{ control.translations.clearDialogMessage }} + + + + + {{ control.translations.clearDialogDecline }} + + + {{ control.translations.clearDialogAccept }} + + + + + + + + \ No newline at end of file diff --git a/packages/vue/vue-vanilla/src/complex/components/CombinatorProperties.vue b/packages/vue/vue-vanilla/src/complex/components/CombinatorProperties.vue new file mode 100644 index 000000000..744b0a5fb --- /dev/null +++ b/packages/vue/vue-vanilla/src/complex/components/CombinatorProperties.vue @@ -0,0 +1,67 @@ + + + + + + + diff --git a/packages/vue/vue-vanilla/src/complex/index.ts b/packages/vue/vue-vanilla/src/complex/index.ts new file mode 100644 index 000000000..f03ad574a --- /dev/null +++ b/packages/vue/vue-vanilla/src/complex/index.ts @@ -0,0 +1,7 @@ +export { default as ObjectRenderer } from './ObjectRenderer.vue'; +export { default as OneOfRenderer } from './OneOfRenderer.vue'; + +import { entry as objectRendererEntry } from './ObjectRenderer.vue'; +import { entry as oneOfRendererEntry } from './OneOfRenderer.vue'; + +export const complexRenderers = [objectRendererEntry, oneOfRendererEntry]; diff --git a/packages/vue/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue b/packages/vue/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue index 7dd3855a4..0b520279d 100644 --- a/packages/vue/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue +++ b/packages/vue/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue @@ -62,6 +62,6 @@ export default controlRenderer; export const entry: JsonFormsRendererRegistryEntry = { renderer: controlRenderer, - tester: rankWith(2, isOneOfEnumControl), + tester: rankWith(5, isOneOfEnumControl), }; diff --git a/packages/vue/vue-vanilla/src/renderers.ts b/packages/vue/vue-vanilla/src/renderers.ts index 18465cc74..2b2efd287 100644 --- a/packages/vue/vue-vanilla/src/renderers.ts +++ b/packages/vue/vue-vanilla/src/renderers.ts @@ -1,4 +1,5 @@ import { arrayRenderers } from './array'; +import { complexRenderers } from './complex'; import { controlRenderers } from './controls'; import { labelRenderers } from './label'; import { layoutRenderers } from './layouts'; @@ -6,6 +7,7 @@ import { layoutRenderers } from './layouts'; export const vanillaRenderers = [ ...controlRenderers, ...layoutRenderers, + ...complexRenderers, ...arrayRenderers, ...labelRenderers, ]; diff --git a/packages/vue/vue-vanilla/src/styles/defaultStyles.ts b/packages/vue/vue-vanilla/src/styles/defaultStyles.ts index f7515d2e3..7a871c0d9 100644 --- a/packages/vue/vue-vanilla/src/styles/defaultStyles.ts +++ b/packages/vue/vue-vanilla/src/styles/defaultStyles.ts @@ -44,4 +44,12 @@ export const defaultStyles: Styles = { label: { root: 'label-element', }, + dialog: { + root: 'dialog-root', + title: 'dialog-title', + body: 'dialog-body', + actions: 'dialog-actions', + buttonPrimary: 'dialog-button-primary', + buttonSecondary: 'dialog-button-secondary', + }, }; diff --git a/packages/vue/vue-vanilla/src/styles/styles.ts b/packages/vue/vue-vanilla/src/styles/styles.ts index 292f43d21..06d155360 100644 --- a/packages/vue/vue-vanilla/src/styles/styles.ts +++ b/packages/vue/vue-vanilla/src/styles/styles.ts @@ -10,6 +10,7 @@ const createEmptyStyles = (): Styles => ({ group: {}, arrayList: {}, label: {}, + dialog: {}, }); export interface Styles { @@ -24,6 +25,14 @@ export interface Styles { select?: string; option?: string; }; + dialog: { + root?: string; + title?: string; + body?: string; + actions?: string; + buttonPrimary?: string; + buttonSecondary?: string; + }; verticalLayout: { root?: string; item?: string; diff --git a/packages/vue/vue-vanilla/tests/unit/complex/ObjectRenderer.spec.ts b/packages/vue/vue-vanilla/tests/unit/complex/ObjectRenderer.spec.ts new file mode 100644 index 000000000..2f710e746 --- /dev/null +++ b/packages/vue/vue-vanilla/tests/unit/complex/ObjectRenderer.spec.ts @@ -0,0 +1,52 @@ +import { expect } from 'chai'; +import { mountJsonForms } from '../util'; + +const schema = { + properties: { + nested: { + title: 'My Object', + properties: { + a: { + type: 'string', + }, + b: { + type: 'boolean', + }, + }, + }, + }, +}; +const uischema = { + type: 'Control', + scope: '#', +}; + +describe('ObjectRenderer.vue', () => { + it('renders a fieldset', () => { + const wrapper = mountJsonForms( + { nested: { a: 'a', b: true } }, + schema, + uischema + ); + expect(wrapper.find('fieldset').exists()).to.be.true; + }); + + it('renders group label', () => { + const wrapper = mountJsonForms( + { nested: { a: 'a', b: true } }, + schema, + uischema + ); + expect(wrapper.find('legend').text()).to.equal('My Object'); + }); + + it('renders children', () => { + const wrapper = mountJsonForms( + { nested: { a: 'a', b: true } }, + schema, + uischema + ); + const inputs = wrapper.findAll('input'); + expect(inputs.length).to.equal(2); + }); +}); diff --git a/packages/vue/vue-vanilla/tests/unit/complex/OneOfRenderer.spec.ts b/packages/vue/vue-vanilla/tests/unit/complex/OneOfRenderer.spec.ts new file mode 100644 index 000000000..95ac68d33 --- /dev/null +++ b/packages/vue/vue-vanilla/tests/unit/complex/OneOfRenderer.spec.ts @@ -0,0 +1,57 @@ +import { expect } from 'chai'; +import { mountJsonForms } from '../util'; + +const schema = { + title: 'My Object', + oneOf: [ + { + title: 'Variant A', + properties: { + variant: { + const: 'a', + }, + a: { + type: 'string', + }, + }, + }, + { + title: 'Variant B', + properties: { + variant: { + const: 'b', + }, + b: { + type: 'string', + }, + }, + }, + ], +}; +const uischema = { + type: 'Control', + scope: '#', +}; + +describe('OneOfRenderer.vue', () => { + it('renders select label', () => { + const wrapper = mountJsonForms({ variant: 'b', b: 'b' }, schema, uischema); + expect(wrapper.find('label').text()).to.equal('My Object'); + }); + + it('renders select', () => { + const wrapper = mountJsonForms({ variant: 'b', b: 'b' }, schema, uischema); + const select = wrapper.find('select'); + expect(select.element.value).to.equal('1'); + expect( + select.findAll('option').map((option) => option.element.label) + ).to.eql(['Variant A', 'Variant B']); + }); + + it('renders variant-specific children only', () => { + const wrapper = mountJsonForms({ variant: 'b', b: 'b' }, schema, uischema); + const inputs = wrapper.findAll('input'); + expect(inputs.length).to.equal(1); + expect(inputs[0].element.value).to.equal('b'); + }); +});
+ {{ control.translations.clearDialogMessage }} +