Skip to content

Commit

Permalink
Chore/12153 component schema v4 update (#12351)
Browse files Browse the repository at this point in the history
* updated script

* add back object properties and reorder properties

* update checks for expression refs

* added missing null checks

* added some more tests

* refactor FormComponentConfig

* small tweaks

* Update frontend/packages/ux-editor/src/utils/component.ts

Co-authored-by: andreastanderen <71079896+standeren@users.noreply.github.com>

* remove unused method

---------

Co-authored-by: andreastanderen <71079896+standeren@users.noreply.github.com>
  • Loading branch information
nkylstad and standeren authored Feb 22, 2024
1 parent 2c0572a commit 456e042
Show file tree
Hide file tree
Showing 23 changed files with 538 additions and 215 deletions.
1 change: 1 addition & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,7 @@
"ux_editor.component_properties.minNumberOfAttachments": "Minimum antall vedlegg",
"ux_editor.component_properties.mode": "Modus",
"ux_editor.component_properties.openInNewTab": "Lenken skal åpnes i ny fane",
"ux_editor.component_properties.optionalIndicator": "Vis valgfri-indikator på ledetekst",
"ux_editor.component_properties.options": "Alternativer",
"ux_editor.component_properties.optionsId": "Kodeliste",
"ux_editor.component_properties.pageBreak": "PDF-innstillinger (pageBreak)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,87 @@ describe('FormComponentConfig', () => {
expect(screen.queryByText('nullProperty')).not.toBeInTheDocument();
});

it('should render nothing if schema is undefined', () => {
render({
props: {
schema: undefined,
},
});
expect(
screen.queryByText(textMock(`ux_editor.component_properties.grid`)),
).not.toBeInTheDocument();
});

it('should render nothing if schema properties are undefined', () => {
render({
props: {
schema: {
properties: undefined,
},
},
});
expect(
screen.queryByText(textMock(`ux_editor.component_properties.grid`)),
).not.toBeInTheDocument();
});

it('should not render property if it is unsupported', () => {
render({
props: {
schema: {
...InputSchema,
properties: {
...InputSchema.properties,
unsupportedProperty: {
type: 'object',
properties: {},
additionalProperties: {
type: 'string',
},
},
},
},
},
});
expect(
screen.queryByText(textMock(`ux_editor.component_properties.unsupportedProperty`)),
).not.toBeInTheDocument();
});

it('should only render array properties with items of type string AND enum values', () => {
render({
props: {
schema: {
...InputSchema,
properties: {
...InputSchema.properties,
supportedArrayProperty: {
type: 'array',
items: {
type: 'string',
enum: ['option1', 'option2'],
},
},
unsupportedArrayProperty: {
type: 'array',
items: {
type: 'string',
},
},
},
},
},
});
expect(
screen.getByRole('combobox', {
name: textMock(`ux_editor.component_properties.supportedArrayProperty`),
}),
).toBeInTheDocument();
expect(
screen.queryByLabelText(textMock(`ux_editor.component_properties.unsupportedArrayProperty`)),
).not.toBeInTheDocument();
});

const render = ({
props = {},
queries = {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import { Alert, Heading } from '@digdir/design-system-react';
import { Alert, Card, Heading, Paragraph } from '@digdir/design-system-react';
import type { FormComponent } from '../../types/FormComponent';
import { EditBooleanValue } from './editModal/EditBooleanValue';
import { EditNumberValue } from './editModal/EditNumberValue';
import { EditStringValue } from './editModal/EditStringValue';
import { useText } from '../../hooks';
import { useComponentPropertyLabel, useText } from '../../hooks';
import {
ExpressionSchemaBooleanDefinitionReference,
getUnsupportedPropertyTypes,
PropertyTypes,
propertyKeysToExcludeFromComponentConfig,
getSupportedPropertyKeysForPropertyType,
} from '../../utils/component';
import { EditGrid } from './editModal/EditGrid';
import type { FormItem } from '../../types/FormItem';
Expand All @@ -22,6 +23,7 @@ export interface FormComponentConfigProps extends IEditFormComponentProps {
schema: any;
hideUnsupported?: boolean;
}

export const FormComponentConfig = ({
schema,
editFormId,
Expand All @@ -30,26 +32,53 @@ export const FormComponentConfig = ({
hideUnsupported,
}: FormComponentConfigProps) => {
const t = useText();
const componentPropertyLabel = useComponentPropertyLabel();

if (!schema?.properties) return null;

const {
children,
dataModelBindings,
required,
readOnly,
id,
textResourceBindings,
type,
options,
optionsId,
hasCustomFileEndings,
validFileEndings,
grid,
...rest
} = schema.properties;
const { properties } = schema;
const { hasCustomFileEndings, validFileEndings, grid } = properties;

// Add any properties that have a custom implementation to this list so they are not duplicated in the generic view
const customProperties = ['hasCustomFileEndings', 'validFileEndings', 'grid', 'children'];

const booleanPropertyKeys: string[] = getSupportedPropertyKeysForPropertyType(
schema.properties,
[PropertyTypes.boolean],
customProperties,
);
const stringPropertyKeys: string[] = getSupportedPropertyKeysForPropertyType(
schema.properties,
[PropertyTypes.string],
customProperties,
);
const numberPropertyKeys: string[] = getSupportedPropertyKeysForPropertyType(
schema.properties,
[PropertyTypes.number, PropertyTypes.integer],
customProperties,
);
const arrayPropertyKeys: string[] = getSupportedPropertyKeysForPropertyType(
schema.properties,
[PropertyTypes.array],
customProperties,
);
const objectPropertyKeys: string[] = getSupportedPropertyKeysForPropertyType(
schema.properties,
[PropertyTypes.object],
[...customProperties, 'source'],
);

const unsupportedPropertyKeys: string[] = getUnsupportedPropertyTypes(rest);
const unsupportedPropertyKeys: string[] = Object.keys(properties).filter((key) => {
return (
!booleanPropertyKeys.includes(key) &&
!stringPropertyKeys.includes(key) &&
!numberPropertyKeys.includes(key) &&
!arrayPropertyKeys.includes(key) &&
!objectPropertyKeys.includes(key) &&
!customProperties.includes(key) &&
!propertyKeysToExcludeFromComponentConfig.includes(key)
);
});

return (
<>
Expand All @@ -71,6 +100,20 @@ export const FormComponentConfig = ({
</Heading>
)}

{/** Boolean fields, incl. expression type */}
{booleanPropertyKeys.map((propertyKey) => {
return (
<EditBooleanValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={properties[propertyKey]?.description}
/>
);
})}

{/** Custom logic for custom file endings */}
{hasCustomFileEndings && (
<>
<EditBooleanValue
Expand Down Expand Up @@ -99,76 +142,73 @@ export const FormComponentConfig = ({
</>
)}

{readOnly && (
<EditBooleanValue
propertyKey='readOnly'
helpText={readOnly.description}
component={component}
handleComponentChange={handleComponentUpdate}
/>
)}
{required && (
<EditBooleanValue
propertyKey='required'
helpText={required.description}
component={component}
handleComponentChange={handleComponentUpdate}
/>
)}
{/** String properties */}
{stringPropertyKeys.map((propertyKey) => {
return (
<EditStringValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={properties[propertyKey]?.description}
enumValues={properties[propertyKey]?.enum || properties[propertyKey]?.examples}
/>
);
})}

{Object.keys(rest).map((propertyKey) => {
if (!rest[propertyKey]) return null;
if (
rest[propertyKey].type === 'boolean' ||
rest[propertyKey].$ref?.endsWith(ExpressionSchemaBooleanDefinitionReference)
) {
return (
<EditBooleanValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={rest[propertyKey]?.description}
/>
);
}
if (rest[propertyKey].type === 'number' || rest[propertyKey].type === 'integer') {
return (
<EditNumberValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={rest[propertyKey]?.description}
/>
);
}
if (rest[propertyKey].type === 'string') {
return (
<EditStringValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={rest[propertyKey]?.description}
enumValues={rest[propertyKey]?.enum}
/>
);
}
if (rest[propertyKey].type === 'array' && rest[propertyKey].items?.type === 'string') {
return (
<EditStringValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
{/** Number properties (number and integer types) */}
{numberPropertyKeys.map((propertyKey) => {
return (
<EditNumberValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={properties[propertyKey]?.description}
/>
);
})}

{/** Array properties with enum values) */}
{arrayPropertyKeys.map((propertyKey) => {
return (
<EditStringValue
component={component}
handleComponentChange={handleComponentUpdate}
propertyKey={propertyKey}
key={propertyKey}
helpText={properties[propertyKey]?.description}
enumValues={properties[propertyKey]?.items?.enum}
multiple={true}
/>
);
})}

{/** Object properties */}
{objectPropertyKeys.map((propertyKey) => {
return (
<Card key={propertyKey}>
<Heading level={3} size='xxsmall'>
{componentPropertyLabel(propertyKey)}
</Heading>
{properties[propertyKey]?.description && (
<Paragraph size='small'>{properties[propertyKey].description}</Paragraph>
)}
<FormComponentConfig
key={propertyKey}
helpText={rest[propertyKey]?.description}
enumValues={rest[propertyKey]?.items?.enum}
multiple={true}
schema={properties[propertyKey]}
component={component[propertyKey] || {}}
handleComponentUpdate={(updatedComponent: FormComponent) => {
handleComponentUpdate({
...component,
[propertyKey]: updatedComponent,
});
}}
editFormId={editFormId}
hideUnsupported
/>
);
}
return null;
</Card>
);
})}
{/* Show information about unsupported properties if there are any */}
{unsupportedPropertyKeys.length > 0 && !hideUnsupported && (
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/ux-editor/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { useText } from './useText';
export { useTextResourcesSelector } from './useTextResourcesSelector';
export type { ComponentValidationResult, ErrorCode } from './useValidateComponent';
export { useValidateComponent } from './useValidateComponent';
export { useComponentPropertyLabel } from './useComponentPropertyLabel';
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"Required",
"AllExceptRequired",
"All"
]
],
"type": "string"
}
},
"renderAsSummary": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"Required",
"AllExceptRequired",
"All"
]
],
"type": "string"
}
},
"renderAsSummary": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"Required",
"AllExceptRequired",
"All"
]
],
"type": "string"
}
},
"renderAsSummary": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"Required",
"AllExceptRequired",
"All"
]
],
"type": "string"
}
},
"renderAsSummary": {
Expand Down
Loading

0 comments on commit 456e042

Please sign in to comment.