From 06d91c3236f4669098e55d19ca90edd19d5f92bb Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 13 May 2026 15:14:51 -0400 Subject: [PATCH] feat(semantic layers): form for semantic layer with single semantic view --- .../semanticLayers/jsonFormsHelpers.tsx | 93 ++++++++++++++++++- .../semanticViews/AddSemanticViewModal.tsx | 32 ++++++- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/features/semanticLayers/jsonFormsHelpers.tsx b/superset-frontend/src/features/semanticLayers/jsonFormsHelpers.tsx index 85c7b891dbc..7368f61ceec 100644 --- a/superset-frontend/src/features/semanticLayers/jsonFormsHelpers.tsx +++ b/superset-frontend/src/features/semanticLayers/jsonFormsHelpers.tsx @@ -255,22 +255,107 @@ const EnumNamesRenderer = withJsonFormsControlProps(EnumNamesControl); const enumNamesEntry = { // Rank 5: higher than the default string renderer (2–3) so this fires // whenever x-enumNames is present, regardless of the underlying type. + // Array-of-enum schemas are handled by ``multiEnumEntry`` below — this + // renderer only targets scalar string/number controls. tester: rankWith( 5, - schemaMatches(s => { - const names = (s as Record)['x-enumNames']; - return Array.isArray(names) && (names as unknown[]).length > 0; - }), + and( + schemaMatches(s => { + const names = (s as Record)['x-enumNames']; + return Array.isArray(names) && (names as unknown[]).length > 0; + }), + schemaMatches( + s => (s as Record)?.type !== 'array', + ), + ), ), renderer: EnumNamesRenderer, }; +/** + * Renderer for ``{type: 'array', items: {enum: [...]}}`` schemas. Renders + * a single Antd Select with ``mode="multiple"`` (tag-style multi-select), + * matching the natural expectation of a "pick several from a list" control. + * + * Without this, the default ``PrimitiveArrayControl`` from the upstream + * library renders an "Add …" button that creates one single-select per + * element — visually wrong for an enum multi-select and unable to display + * ``items.x-enumNames`` labels. + * + * The renderer is dynamic-aware: when the host form is refreshing the + * schema (e.g. compatible options narrowing as the user picks), the Select + * shows a loading indicator without becoming disabled, so the user can + * continue editing while options refresh. + */ +function MultiEnumControl(props: ControlProps) { + const { refreshingSchema } = props.config ?? {}; + const arraySchema = props.schema as Record; + const itemsSchema = + (arraySchema.items as Record) ?? + ({} as Record); + + const enumValues = (itemsSchema.enum as unknown[]) ?? []; + const enumNames = + (itemsSchema['x-enumNames'] as string[]) ?? enumValues.map(String); + + const options = enumValues.map((value, index) => ({ + value: value as string | number, + label: enumNames[index] ?? String(value), + })); + + const value = Array.isArray(props.data) ? (props.data as unknown[]) : []; + + const tooltip = (props.uischema?.options as Record) + ?.tooltip as string | undefined; + + return ( + +