Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the translation of enum value used for elementLabelProp in ExpandPanelRenderer #2289

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/core/src/models/uischema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,7 @@ export const isLabelable = (obj: unknown): obj is Labelable =>

export const isLabeled = <T = never>(obj: unknown): obj is Labeled<T> =>
isLabelable(obj) && ['string', 'boolean'].includes(typeof obj.label);

export const isControlElement = (
uiSchema: UISchemaElement
): uiSchema is ControlElement => uiSchema.type === 'Control';
10 changes: 2 additions & 8 deletions packages/core/src/testers/testers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import type {
import {
deriveTypes,
hasType,
isEnumSchema,
isOneOfEnumSchema,
resolveSchema,
} from '../util';
Expand Down Expand Up @@ -369,14 +370,7 @@ export const isOneOfControl = and(
*/
export const isEnumControl = and(
uiTypeIs('Control'),
or(
schemaMatches((schema) =>
Object.prototype.hasOwnProperty.call(schema, 'enum')
),
schemaMatches((schema) =>
Object.prototype.hasOwnProperty.call(schema, 'const')
)
)
schemaMatches((schema) => isEnumSchema(schema))
);

/**
Expand Down
94 changes: 93 additions & 1 deletion packages/core/src/util/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,28 @@

import startCase from 'lodash/startCase';

import type { ControlElement, JsonSchema, LabelDescription } from '../models';
import {
ControlElement,
JsonSchema,
LabelDescription,
UISchemaElement,
} from '../models';
import { decode } from './path';
import { getI18nKeyPrefix, Translator } from '../i18n';
import { Resolve } from './util';
import {
getFirstPrimitiveProp,
isEnumSchema,
isOneOfEnumSchema,
} from './schema';
import get from 'lodash/get';
import { findUiControl, getPropPath } from './uischema';
import {
EnumOption,
enumToEnumOptionMapper,
oneOfToEnumOptionMapper,
} from './renderer';
import isEqual from 'lodash/isEqual';

const deriveLabel = (
controlElement: ControlElement,
Expand Down Expand Up @@ -81,3 +101,75 @@ const labelDescription = (text: string, show: boolean): LabelDescription => ({
text: text,
show: show,
});

/**
* Compute the child label title for array based controls
* @param data the data of the control
* @param childPath the child path
* @param childLabelProp the dotted path to the value used as child label
* @param {JsonSchema} schema the json schema for this control
* @param {JsonSchema} rootSchema the root json schema
* @param {Translator} translateFct the translator fonction
* @param {UISchemaElement} uiSchema the uiSchema of the control
*/
export const computeChildLabel = (
data: any,
childPath: string,
childLabelProp: string,
schema: JsonSchema,
rootSchema: JsonSchema,
translateFct: Translator,
uiSchema: UISchemaElement
): string => {
const childData = Resolve.data(data, childPath);

if (!childLabelProp) {
childLabelProp = getFirstPrimitiveProp(schema);
}

// return early in case there is no prop we can query
if (!childLabelProp) {
return '';
}

const currentValue = get(childData, childLabelProp, '');

// check whether the value is part of a oneOf or enum and needs to be translated
const childSchema = Resolve.schema(
schema,
'#' + getPropPath(childLabelProp),
rootSchema
);

let enumOption: EnumOption = undefined;
if (isEnumSchema(childSchema)) {
enumOption = enumToEnumOptionMapper(
currentValue,
translateFct,
getI18nKeyPrefix(
childSchema,
findUiControl(uiSchema, childLabelProp),
childPath + '.' + childLabelProp
)
);
} else if (isOneOfEnumSchema(childSchema)) {
const oneOfArray = childSchema.oneOf as JsonSchema[];
const oneOfSchema = oneOfArray.find((e: JsonSchema) =>
isEqual(e.const, currentValue)
);

if (oneOfSchema) {
enumOption = oneOfToEnumOptionMapper(
oneOfSchema,
translateFct,
getI18nKeyPrefix(
oneOfSchema,
undefined,
childPath + '.' + childLabelProp
)
);
}
}

return enumOption ? enumOption.label : currentValue;
};
9 changes: 9 additions & 0 deletions packages/core/src/util/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@ export const isOneOfEnumSchema = (schema: JsonSchema) =>
Object.prototype.hasOwnProperty.call(schema, 'oneOf') &&
schema.oneOf &&
(schema.oneOf as JsonSchema[]).every((s) => s.const !== undefined);

/**
* Tests whether the schema has an enum.
*/
export const isEnumSchema = (schema: JsonSchema) =>
!!schema &&
typeof schema === 'object' &&
(Object.prototype.hasOwnProperty.call(schema, 'enum') ||
Object.prototype.hasOwnProperty.call(schema, 'const'));
48 changes: 47 additions & 1 deletion packages/core/src/util/uischema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
*/

import isEmpty from 'lodash/isEmpty';
import { isLayout, UISchemaElement } from '../models';
import {
isControlElement,
isLayout,
isScoped,
UISchemaElement,
} from '../models';
import { encode } from './path';

export type IterateCallback = (uischema: UISchemaElement) => void;

Expand Down Expand Up @@ -55,3 +61,43 @@ export const iterateSchema = (
}
toApply(uischema);
};

/**
* Transform a dotted path to a uiSchema properties path
* @param path a dotted prop path to a schema value (i.e. articles.comment.author)
* @return the uiSchema properties path (i.e. /properties/articles/properties/comment/properties/author)
*/
export const getPropPath = (path: string): string => {
return `/properties/${path
.split('.')
.map((p) => encode(p))
.join('/properties/')}`;
};

/**
* Find a control in a uiSchema, based on the dotted path of the schema value
* @param {UISchemaElement} uiSchema the uiSchema to search from
* @param path a dotted prop path to a schema value (i.e. articles.comment.author)
* @return {UISchemaElement} or undefined if not found
*/
export const findUiControl = (
uiSchema: UISchemaElement,
path: string
): UISchemaElement | undefined => {
if (isControlElement(uiSchema)) {
if (isScoped(uiSchema) && uiSchema.scope.endsWith(getPropPath(path))) {
return uiSchema;
} else if (uiSchema.options?.detail) {
return findUiControl(uiSchema.options.detail, path);
}
}

if (isLayout(uiSchema)) {
for (const elem of uiSchema.elements) {
const result = findUiControl(elem, path);
if (result !== undefined) return result;
}
}

return undefined;
};
Loading
Loading