diff --git a/packages/vanilla-renderers/src/complex/categorization/CategorizationList.tsx b/packages/vanilla-renderers/src/complex/categorization/CategorizationList.tsx
index 7f53925b48..b723413fa2 100644
--- a/packages/vanilla-renderers/src/complex/categorization/CategorizationList.tsx
+++ b/packages/vanilla-renderers/src/complex/categorization/CategorizationList.tsx
@@ -24,12 +24,14 @@
*/
import React, { useMemo } from 'react';
import {
- Categorization,
Category,
+ Categorization,
deriveLabelForUISchemaElement,
Translator,
+ isVisible,
} from '@jsonforms/core';
import { isCategorization } from './tester';
+import { AjvProps } from '../../util';
const getCategoryClassName = (
category: Category,
@@ -37,9 +39,10 @@ const getCategoryClassName = (
): string => (selectedCategory === category ? 'selected' : '');
export interface CategorizationProps {
- categorization: Categorization;
+ elements: (Category | Categorization)[];
selectedCategory: Category;
depth: number;
+ data: any;
onSelect: any;
subcategoriesClassName: string;
groupClassName: string;
@@ -47,32 +50,39 @@ export interface CategorizationProps {
}
export const CategorizationList = ({
- categorization,
selectedCategory,
+ elements,
+ data,
depth,
onSelect,
subcategoriesClassName,
groupClassName,
t,
-}: CategorizationProps) => {
+ ajv,
+}: CategorizationProps & AjvProps) => {
+ const filteredElements = useMemo(() => {
+ return elements.filter((category: Category | Categorization) =>
+ isVisible(category, data, undefined, ajv)
+ );
+ }, [elements, data, ajv]);
+
const categoryLabels = useMemo(
- () =>
- categorization.elements.map((cat) =>
- deriveLabelForUISchemaElement(cat, t)
- ),
- [categorization, t]
+ () => filteredElements.map((cat) => deriveLabelForUISchemaElement(cat, t)),
+ [filteredElements, t]
);
return (
- {categorization.elements.map((category, idx) => {
+ {filteredElements.map((category, idx) => {
if (isCategorization(category)) {
return (
-
{categoryLabels[idx]}
{categoryLabels[idx]}
diff --git a/packages/vanilla-renderers/src/complex/categorization/CategorizationRenderer.tsx b/packages/vanilla-renderers/src/complex/categorization/CategorizationRenderer.tsx
index ada4ffc417..47bcb5f370 100644
--- a/packages/vanilla-renderers/src/complex/categorization/CategorizationRenderer.tsx
+++ b/packages/vanilla-renderers/src/complex/categorization/CategorizationRenderer.tsx
@@ -22,89 +22,102 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-import React from 'react';
+import React, { useState } from 'react';
import type { Categorization, Category, LayoutProps } from '@jsonforms/core';
import {
- RendererComponent,
TranslateProps,
withJsonFormsLayoutProps,
withTranslateProps,
} from '@jsonforms/react';
import { CategorizationList } from './CategorizationList';
import { SingleCategory } from './SingleCategory';
-import { isCategorization } from './tester';
-import { withVanillaControlProps } from '../../util';
-import type { VanillaRendererProps } from '../../index';
+import { withAjvProps, withVanillaControlProps } from '../../util';
+import type { AjvProps, VanillaRendererProps } from '../../util';
export interface CategorizationState {
selectedCategory: Category;
}
-class CategorizationRenderer extends RendererComponent<
- LayoutProps & VanillaRendererProps & TranslateProps,
- CategorizationState
-> {
- onCategorySelected = (category: Category) => () => {
- return this.setState({ selectedCategory: category });
- };
+interface CategorizationProps {
+ selected?: number;
+ onChange?(selected: number, prevSelected: number): void;
+}
- /**
- * @inheritDoc
- */
- render() {
- const { uischema, visible, getStyleAsClassName, t } = this.props;
- const categorization = uischema as Categorization;
- const classNames = getStyleAsClassName('categorization');
- const masterClassNames = getStyleAsClassName('categorization.master');
- const detailClassNames = getStyleAsClassName('categorization.detail');
- const selectedCategory = this.findCategory(categorization);
- const subcategoriesClassName = getStyleAsClassName(
- 'category.subcategories'
- );
- const groupClassName = getStyleAsClassName('category.group');
+export const CategorizationRenderer = ({
+ data,
+ uischema,
+ schema,
+ path,
+ selected,
+ t,
+ visible,
+ getStyleAsClassName,
+ onChange,
+ ajv,
+}: LayoutProps &
+ VanillaRendererProps &
+ TranslateProps &
+ CategorizationProps &
+ AjvProps) => {
+ const categorization = uischema as Categorization;
+ const elements = categorization.elements as (Category | Categorization)[];
+ const classNames = getStyleAsClassName('categorization');
+ const masterClassNames = getStyleAsClassName('categorization.master');
+ const detailClassNames = getStyleAsClassName('categorization.detail');
+ const subcategoriesClassName = getStyleAsClassName('category.subcategories');
+ const groupClassName = getStyleAsClassName('category.group');
- return (
-
- );
- }
+ const [previousCategorization, setPreviousCategorization] =
+ useState(uischema as Categorization);
+ const [activeCategory, setActiveCategory] = useState(selected ?? 0);
- private findCategory(categorization: Categorization): Category {
- const category = categorization.elements[0];
+ const safeCategory =
+ activeCategory >= categorization.elements.length ? 0 : activeCategory;
- if (this.state && this.state.selectedCategory) {
- return this.state.selectedCategory;
- }
+ if (categorization !== previousCategorization) {
+ setActiveCategory(0);
+ setPreviousCategorization(categorization);
+ }
- if (isCategorization(category)) {
- return this.findCategory(category);
+ const onCategorySelected = (categoryIndex: number) => () => {
+ if (onChange) {
+ return onChange(categoryIndex, safeCategory);
}
+ return setActiveCategory(categoryIndex);
+ };
- return category;
- }
-}
+ return (
+
+ );
+};
-export default withVanillaControlProps(
- withTranslateProps(withJsonFormsLayoutProps(CategorizationRenderer))
+export default withAjvProps(
+ withVanillaControlProps(
+ withTranslateProps(withJsonFormsLayoutProps(CategorizationRenderer))
+ )
);
diff --git a/packages/vanilla-renderers/src/index.ts b/packages/vanilla-renderers/src/index.ts
index bdd518bfc9..18ad3c941e 100644
--- a/packages/vanilla-renderers/src/index.ts
+++ b/packages/vanilla-renderers/src/index.ts
@@ -22,89 +22,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-import { RankedTester } from '@jsonforms/core';
-
-import {
- BooleanCell,
- booleanCellTester,
- DateCell,
- dateCellTester,
- dateTimeCellTester,
- EnumCell,
- enumCellTester,
- IntegerCell,
- integerCellTester,
- NumberCell,
- numberCellTester,
- SliderCell,
- sliderCellTester,
- TextAreaCell,
- textAreaCellTester,
- TextCell,
- textCellTester,
- TimeCell,
- timeCellTester,
-} from './cells';
-
-import {
- InputControl,
- inputControlTester,
- RadioGroupControl,
- radioGroupControlTester,
- OneOfRadioGroupControl,
- oneOfRadioGroupControlTester,
-} from './controls';
-
-import {
- ArrayControl,
- arrayControlTester,
- Categorization,
- categorizationTester,
- LabelRenderer,
- labelRendererTester,
- TableArrayControl,
- tableArrayControlTester,
-} from './complex';
-
-import {
- GroupLayout,
- groupTester,
- HorizontalLayout,
- horizontalLayoutTester,
- VerticalLayout,
- verticalLayoutTester,
-} from './layouts';
-import DateTimeCell from './cells/DateTimeCell';
-
-export interface WithClassname {
- className?: string;
-}
-
-/**
- * Additional renderer props specific to vanilla renderers.
- */
-export interface VanillaRendererProps extends WithClassname {
- classNames?: { [className: string]: string };
- /**
- * Returns all classes associated with the given style.
- * @param {string} string the style name
- * @param args any additional args necessary to calculate the classes
- * @returns {string[]} array of class names
- */
- getStyle?(string: string, ...args: any[]): string[];
-
- /**
- * Returns all classes associated with the given style as a single class name.
- * @param {string} string the style name
- * @param args any additional args necessary to calculate the classes
- * @returns {string[]} array of class names
- */
- getStyleAsClassName?(string: string, ...args: any[]): string;
-}
-
-export interface WithChildren {
- children: any;
-}
export * from './actions';
export * from './controls';
@@ -114,29 +31,4 @@ export * from './layouts';
export * from './reducers';
export * from './util';
export * from './styles';
-
-export const vanillaRenderers: { tester: RankedTester; renderer: any }[] = [
- { tester: inputControlTester, renderer: InputControl },
- { tester: radioGroupControlTester, renderer: RadioGroupControl },
- { tester: oneOfRadioGroupControlTester, renderer: OneOfRadioGroupControl },
- { tester: arrayControlTester, renderer: ArrayControl },
- { tester: labelRendererTester, renderer: LabelRenderer },
- { tester: categorizationTester, renderer: Categorization },
- { tester: tableArrayControlTester, renderer: TableArrayControl },
- { tester: groupTester, renderer: GroupLayout },
- { tester: verticalLayoutTester, renderer: VerticalLayout },
- { tester: horizontalLayoutTester, renderer: HorizontalLayout },
-];
-
-export const vanillaCells: { tester: RankedTester; cell: any }[] = [
- { tester: booleanCellTester, cell: BooleanCell },
- { tester: dateCellTester, cell: DateCell },
- { tester: dateTimeCellTester, cell: DateTimeCell },
- { tester: enumCellTester, cell: EnumCell },
- { tester: integerCellTester, cell: IntegerCell },
- { tester: numberCellTester, cell: NumberCell },
- { tester: sliderCellTester, cell: SliderCell },
- { tester: textAreaCellTester, cell: TextAreaCell },
- { tester: textCellTester, cell: TextCell },
- { tester: timeCellTester, cell: TimeCell },
-];
+export * from './renderers';
diff --git a/packages/vanilla-renderers/src/renderers.ts b/packages/vanilla-renderers/src/renderers.ts
new file mode 100644
index 0000000000..a432d5db47
--- /dev/null
+++ b/packages/vanilla-renderers/src/renderers.ts
@@ -0,0 +1,104 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+import { RankedTester } from '@jsonforms/core';
+
+import {
+ BooleanCell,
+ booleanCellTester,
+ DateCell,
+ dateCellTester,
+ DateTimeCell,
+ dateTimeCellTester,
+ EnumCell,
+ enumCellTester,
+ IntegerCell,
+ integerCellTester,
+ NumberCell,
+ numberCellTester,
+ SliderCell,
+ sliderCellTester,
+ TextAreaCell,
+ textAreaCellTester,
+ TextCell,
+ textCellTester,
+ TimeCell,
+ timeCellTester,
+} from './cells';
+
+import {
+ InputControl,
+ inputControlTester,
+ RadioGroupControl,
+ radioGroupControlTester,
+ OneOfRadioGroupControl,
+ oneOfRadioGroupControlTester,
+} from './controls';
+
+import {
+ ArrayControl,
+ arrayControlTester,
+ Categorization,
+ categorizationTester,
+ LabelRenderer,
+ labelRendererTester,
+ TableArrayControl,
+ tableArrayControlTester,
+} from './complex';
+
+import {
+ GroupLayout,
+ groupTester,
+ HorizontalLayout,
+ horizontalLayoutTester,
+ VerticalLayout,
+ verticalLayoutTester,
+} from './layouts';
+
+export const vanillaRenderers: { tester: RankedTester; renderer: any }[] = [
+ { tester: inputControlTester, renderer: InputControl },
+ { tester: radioGroupControlTester, renderer: RadioGroupControl },
+ { tester: oneOfRadioGroupControlTester, renderer: OneOfRadioGroupControl },
+ { tester: arrayControlTester, renderer: ArrayControl },
+ { tester: labelRendererTester, renderer: LabelRenderer },
+ { tester: categorizationTester, renderer: Categorization },
+ { tester: tableArrayControlTester, renderer: TableArrayControl },
+ { tester: groupTester, renderer: GroupLayout },
+ { tester: verticalLayoutTester, renderer: VerticalLayout },
+ { tester: horizontalLayoutTester, renderer: HorizontalLayout },
+];
+
+export const vanillaCells: { tester: RankedTester; cell: any }[] = [
+ { tester: booleanCellTester, cell: BooleanCell },
+ { tester: dateCellTester, cell: DateCell },
+ { tester: dateTimeCellTester, cell: DateTimeCell },
+ { tester: enumCellTester, cell: EnumCell },
+ { tester: integerCellTester, cell: IntegerCell },
+ { tester: numberCellTester, cell: NumberCell },
+ { tester: sliderCellTester, cell: SliderCell },
+ { tester: textAreaCellTester, cell: TextAreaCell },
+ { tester: textCellTester, cell: TextCell },
+ { tester: timeCellTester, cell: TimeCell },
+];
diff --git a/packages/vanilla-renderers/src/util/index.ts b/packages/vanilla-renderers/src/util/index.ts
new file mode 100644
index 0000000000..919de7ea85
--- /dev/null
+++ b/packages/vanilla-renderers/src/util/index.ts
@@ -0,0 +1,26 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+export * from './i18nDefaults';
+export * from './props';
diff --git a/packages/vanilla-renderers/src/util/index.tsx b/packages/vanilla-renderers/src/util/props.tsx
similarity index 85%
rename from packages/vanilla-renderers/src/util/index.tsx
rename to packages/vanilla-renderers/src/util/props.tsx
index 511ee2f5f1..52a398331d 100644
--- a/packages/vanilla-renderers/src/util/index.tsx
+++ b/packages/vanilla-renderers/src/util/props.tsx
@@ -22,13 +22,12 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-export * from './i18nDefaults';
+
+import Ajv from 'ajv';
import React, { ComponentType, useMemo } from 'react';
import isEmpty from 'lodash/isEmpty';
-import {
+import type {
ControlElement,
- convertToValidClassName,
- getConfig,
JsonFormsState,
OwnPropsOfCell,
OwnPropsOfControl,
@@ -37,12 +36,46 @@ import {
StatePropsOfCell,
StatePropsOfControl,
} from '@jsonforms/core';
+import { convertToValidClassName, getAjv, getConfig } from '@jsonforms/core';
import { useJsonForms } from '@jsonforms/react';
import { getStyle, getStyleAsClassName } from '../reducers';
-import { VanillaRendererProps } from '../index';
import { findStyle, findStyleAsClassName } from '../reducers/styling';
import { useStyles } from '../styles';
+export interface WithClassname {
+ className?: string;
+}
+
+export interface AjvProps {
+ ajv: Ajv;
+}
+
+export interface WithChildren {
+ children: any;
+}
+
+/**
+ * Additional renderer props specific to vanilla renderers.
+ */
+export interface VanillaRendererProps extends WithClassname {
+ classNames?: { [className: string]: string };
+ /**
+ * Returns all classes associated with the given style.
+ * @param {string} string the style name
+ * @param args any additional args necessary to calculate the classes
+ * @returns {string[]} array of class names
+ */
+ getStyle?(string: string, ...args: any[]): string[];
+
+ /**
+ * Returns all classes associated with the given style as a single class name.
+ * @param {string} string the style name
+ * @param args any additional args necessary to calculate the classes
+ * @returns {string[]} array of class names
+ */
+ getStyleAsClassName?(string: string, ...args: any[]): string;
+}
+
/**
* Add vanilla props to the return value of calling the given
* mapStateToProps function.
@@ -240,6 +273,16 @@ const withVanillaCellPropsForType =
);
};
+export const withAjvProps =
(
+ Component: ComponentType
+) =>
+ function WithAjvProps(props: P) {
+ const ctx = useJsonForms();
+ const ajv = getAjv({ jsonforms: { ...ctx } });
+
+ return ;
+ };
+
export const withVanillaCellProps =
withVanillaCellPropsForType('control.input');
diff --git a/packages/vanilla-renderers/test/renderers/CategorizationRenderer.test.tsx b/packages/vanilla-renderers/test/renderers/CategorizationRenderer.test.tsx
index 2f92951286..ed552bebe0 100644
--- a/packages/vanilla-renderers/test/renderers/CategorizationRenderer.test.tsx
+++ b/packages/vanilla-renderers/test/renderers/CategorizationRenderer.test.tsx
@@ -30,7 +30,7 @@ import {
JsonSchema,
Layout,
} from '@jsonforms/core';
-import { JsonFormsStateProvider } from '@jsonforms/react';
+import { JsonForms, JsonFormsStateProvider } from '@jsonforms/react';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import Enzyme, { mount, ReactWrapper } from 'enzyme';
import CategorizationRenderer, {
@@ -386,4 +386,64 @@ describe('Categorization renderer', () => {
const div: HTMLDivElement = wrapper.find('.categorization').getDOMNode();
expect(div.hidden).toBe(false);
});
+
+ test('reset selected category on schema change', () => {
+ const oldUischema: Categorization = {
+ type: 'Categorization',
+ label: '',
+ elements: [
+ {
+ type: 'Category',
+ label: 'A',
+ elements: [],
+ },
+ {
+ type: 'Category',
+ label: 'B',
+ elements: [],
+ },
+ {
+ type: 'Category',
+ label: 'C',
+ elements: [],
+ },
+ ],
+ };
+ const newUischema: Categorization = {
+ type: 'Categorization',
+ label: '',
+ elements: [
+ {
+ type: 'Category',
+ label: 'A',
+ elements: [],
+ },
+ {
+ type: 'Category',
+ label: 'C',
+ elements: [],
+ },
+ ],
+ };
+
+ wrapper = mount(
+
+ );
+
+ let secondItem = wrapper.find('li').at(1);
+ secondItem.simulate('click');
+ secondItem = wrapper.find('li').at(1);
+ expect(secondItem.hasClass('selected')).toBe(true);
+
+ wrapper.setProps({ uischema: newUischema });
+ wrapper.update();
+
+ secondItem = wrapper.find('li').at(1);
+ expect(secondItem.hasClass('selected')).toBe(false);
+ });
});