Skip to content

Commit b80191e

Browse files
authored
vue-vanilla: Add enum array renderer
Adds an array enum renderer that renders the enum as multiple check boxes. It supports enum specification via `enum` and `oneOf` in the schema's `items` property.
1 parent 9d3f706 commit b80191e

File tree

3 files changed

+219
-1
lines changed

3 files changed

+219
-1
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<template>
2+
<div v-for="(checkElement, index) in control.options" :key="index">
3+
<input
4+
:id="control.id + `-input-${index}`"
5+
type="checkbox"
6+
:class="styles.control.input"
7+
:value="checkElement.value"
8+
:checked="dataHasEnum(checkElement.value)"
9+
:disabled="!control.enabled"
10+
:placeholder="appliedOptions?.placeholder"
11+
@change="(event) => toggle(checkElement.value, event.target.checked)"
12+
/>
13+
<label :for="control.id + `-input-${index}`">
14+
{{ checkElement.label }}
15+
</label>
16+
</div>
17+
</template>
18+
19+
<script lang="ts">
20+
import { defineComponent } from 'vue';
21+
import {
22+
RendererProps,
23+
rendererProps,
24+
useJsonFormsMultiEnumControl,
25+
} from '@jsonforms/vue';
26+
import {
27+
ControlElement,
28+
JsonFormsRendererRegistryEntry,
29+
rankWith,
30+
uiTypeIs,
31+
and,
32+
schemaMatches,
33+
hasType,
34+
schemaSubPathMatches,
35+
JsonSchema,
36+
} from '@jsonforms/core';
37+
import { useVanillaArrayControl } from '../util';
38+
39+
const controlRenderer = defineComponent({
40+
name: 'EnumArrayRenderer',
41+
props: {
42+
...rendererProps<ControlElement>(),
43+
},
44+
setup(props: RendererProps<ControlElement>) {
45+
const control = useJsonFormsMultiEnumControl(props);
46+
47+
return useVanillaArrayControl(control);
48+
},
49+
methods: {
50+
dataHasEnum(value: any): boolean {
51+
return !!this.control.data?.includes(value);
52+
},
53+
toggle(value: any, checked: boolean): void {
54+
if (checked) {
55+
this.addItem(this.control.path, value);
56+
} else {
57+
this.removeItem?.(this.control.path, value);
58+
}
59+
},
60+
},
61+
});
62+
63+
export default controlRenderer;
64+
65+
const hasOneOfItems = (schema: JsonSchema): boolean =>
66+
schema.oneOf !== undefined &&
67+
schema.oneOf.length > 0 &&
68+
(schema.oneOf as JsonSchema[]).every((entry: JsonSchema) => {
69+
return entry.const !== undefined;
70+
});
71+
72+
const hasEnumItems = (schema: JsonSchema): boolean =>
73+
schema.type === 'string' && schema.enum !== undefined;
74+
75+
export const entry: JsonFormsRendererRegistryEntry = {
76+
renderer: controlRenderer,
77+
tester: rankWith(
78+
5,
79+
and(
80+
uiTypeIs('Control'),
81+
and(
82+
schemaMatches(
83+
(schema) =>
84+
hasType(schema, 'array') &&
85+
!Array.isArray(schema.items) &&
86+
schema.uniqueItems === true
87+
),
88+
schemaSubPathMatches('items', (schema) => {
89+
return hasOneOfItems(schema) || hasEnumItems(schema);
90+
})
91+
)
92+
)
93+
),
94+
};
95+
</script>
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
export { default as ObjectRenderer } from './ObjectRenderer.vue';
22
export { default as OneOfRenderer } from './OneOfRenderer.vue';
3+
export { default as EnumArrayRenderer } from './EnumArrayRenderer.vue';
34

45
import { entry as objectRendererEntry } from './ObjectRenderer.vue';
56
import { entry as oneOfRendererEntry } from './OneOfRenderer.vue';
7+
import { entry as enumArrayRendererEntry } from './EnumArrayRenderer.vue';
68

7-
export const complexRenderers = [objectRendererEntry, oneOfRendererEntry];
9+
export const complexRenderers = [
10+
objectRendererEntry,
11+
oneOfRendererEntry,
12+
enumArrayRendererEntry,
13+
];
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { expect } from 'chai';
2+
import { mountJsonForms } from '../util';
3+
4+
const schemaOneOfMultiEnum = {
5+
properties: {
6+
oneOfMultiEnum: {
7+
type: 'array',
8+
uniqueItems: true,
9+
items: {
10+
oneOf: [
11+
{
12+
const: 'foo',
13+
title: 'Foo',
14+
},
15+
{
16+
const: 'bar',
17+
title: 'Bar',
18+
},
19+
{
20+
const: 'foobar',
21+
title: 'FooBar',
22+
},
23+
],
24+
},
25+
},
26+
},
27+
};
28+
29+
const schemaMultiEnum = {
30+
properties: {
31+
multiEnum: {
32+
type: 'array',
33+
uniqueItems: true,
34+
items: {
35+
type: 'string',
36+
enum: ['foo', 'bar', 'foobar'],
37+
},
38+
},
39+
},
40+
};
41+
42+
const uischema = {
43+
type: 'Control',
44+
scope: '#',
45+
};
46+
47+
describe('EnumArrayRenderer.vue oneOfMultiEnum', () => {
48+
it('renders checkboxes', () => {
49+
const wrapper = mountJsonForms(
50+
{ oneOfMultiEnum: ['foo', 'bar'] },
51+
schemaOneOfMultiEnum,
52+
uischema
53+
);
54+
const checkboxes = wrapper.findAll('input[type="checkbox"]');
55+
expect(checkboxes.length).to.equal(3);
56+
});
57+
58+
it('emits a data remove change', async () => {
59+
const wrapper = mountJsonForms(
60+
{ oneOfMultiEnum: ['foo', 'bar'] },
61+
schemaOneOfMultiEnum,
62+
uischema
63+
);
64+
const input = wrapper.find('input[value="foo"]');
65+
await input.trigger('click');
66+
expect(wrapper.vm.data).to.eql({ oneOfMultiEnum: ['bar'] });
67+
});
68+
69+
it('emits a data add change', async () => {
70+
const wrapper = mountJsonForms(
71+
{ oneOfMultiEnum: ['foo', 'bar'] },
72+
schemaOneOfMultiEnum,
73+
uischema
74+
);
75+
const input = wrapper.find('input[value="foobar"]');
76+
await input.trigger('click');
77+
expect(wrapper.vm.data).to.eql({
78+
oneOfMultiEnum: ['foo', 'bar', 'foobar'],
79+
});
80+
});
81+
});
82+
83+
describe('EnumArrayRenderer.vue multiEnum', () => {
84+
it('renders checkboxes', () => {
85+
const wrapper = mountJsonForms(
86+
{ multiEnum: ['foo', 'bar'] },
87+
schemaMultiEnum,
88+
uischema
89+
);
90+
const checkboxes = wrapper.findAll('input[type="checkbox"]');
91+
expect(checkboxes.length).to.equal(3);
92+
});
93+
94+
it('emits a data remove change', async () => {
95+
const wrapper = mountJsonForms(
96+
{ multiEnum: ['foo', 'bar'] },
97+
schemaMultiEnum,
98+
uischema
99+
);
100+
const input = wrapper.find('input[value="foo"]');
101+
await input.trigger('click');
102+
expect(wrapper.vm.data).to.eql({ multiEnum: ['bar'] });
103+
});
104+
105+
it('emits a data add change', async () => {
106+
const wrapper = mountJsonForms(
107+
{ multiEnum: ['foo', 'bar'] },
108+
schemaMultiEnum,
109+
uischema
110+
);
111+
const input = wrapper.find('input[value="foobar"]');
112+
await input.trigger('click');
113+
expect(wrapper.vm.data).to.eql({
114+
multiEnum: ['foo', 'bar', 'foobar'],
115+
});
116+
});
117+
});

0 commit comments

Comments
 (0)