Name a nested fieldset readably instead of by its dotted path
A field inside a nested object whose object declares no name of its own took the raw parent path as its fieldset legend, which the uppercasing CSS then rendered as "LLMDESCRIPTOR.PARAMETERS". The expression `?? parentPath(path)` appeared in four places - twice in generic-node, twice in container-node - plus a fifth in task-step-node and a sixth as the default in schema-driven-fields. That is how one mistake came to be wrong in six places at once, so this is one exported helper rather than six corrected copies. Independent of the optional-group work it was found during, and worth having on its own. 579 frontend tests green; the assertion fails when the helper returns the raw path again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
307552cde1
commit
b53a22dcc4
|
|
@ -23,7 +23,7 @@ import { NodeFocusModalController } from '../node-focus-modal-controller';
|
|||
import { firstValueFrom } from 'rxjs';
|
||||
import { SWIMLANES_ENABLED } from '@shared/feature-flags';
|
||||
import { extractSchemaRequirements, SchemaRequirements } from '../schema-requirements';
|
||||
import { evaluateUiConditionRule, getValueByPath, parentPath, pathToLabel, resolveNodeIcon, resolveSchemaPath, splitTemplatedTextParts, valueToDisplayString } from '../node-utility';
|
||||
import { evaluateUiConditionRule, getValueByPath, parentGroupLabel, pathToLabel, resolveNodeIcon, resolveSchemaPath, splitTemplatedTextParts, valueToDisplayString } from '../node-utility';
|
||||
import {
|
||||
collectSchemaFlowDataFields,
|
||||
flowDataNodeCount,
|
||||
|
|
@ -779,7 +779,7 @@ export class ContainerNodeComponent implements OnDestroy {
|
|||
getFieldValue: (definition, nextConfig) => valueToDisplayString(getValueByPath(nextConfig, definition.path)),
|
||||
isFieldWide: (definition) => definition.ui.widget === 'textarea' || definition.label.length >= 18,
|
||||
getRichContentParts: (path) => this.toRichContentParts(path),
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.containerSchema, path).group ?? parentPath(path)
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.containerSchema, path).group ?? parentGroupLabel(path)
|
||||
});
|
||||
|
||||
const allFields = [
|
||||
|
|
@ -797,7 +797,7 @@ export class ContainerNodeComponent implements OnDestroy {
|
|||
definitions: this.containerFieldDefinitions.filter((field) => !this.isContainerTypeField(field.path)),
|
||||
fields: allFields,
|
||||
richContentFields: allRichContentFields,
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.containerSchema, path).group ?? parentPath(path)
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.containerSchema, path).group ?? parentGroupLabel(path)
|
||||
});
|
||||
this.parameterDisplayItems = ordered.rootItems;
|
||||
this.parameterFieldGroups = ordered.groups;
|
||||
|
|
|
|||
|
|
@ -20,31 +20,7 @@ import { BlocksService } from '@services/blocks/blocks';
|
|||
import { firstValueFrom, take } from 'rxjs';
|
||||
import { SWIMLANES_ENABLED } from '@shared/feature-flags';
|
||||
import { ConditionalRequiredField, extractSchemaRequirements, SchemaRequirements } from '../schema-requirements';
|
||||
import {
|
||||
type UiConditionRule,
|
||||
evaluateUiConditionRule,
|
||||
flattenPrimitiveValues,
|
||||
formatNodeTitle,
|
||||
getOutputPillClass,
|
||||
getOutputsTitle,
|
||||
getValueByPath,
|
||||
isConditionalByPorts,
|
||||
isHumanInteractiveNode,
|
||||
orderedSchemaPropertyEntries,
|
||||
parentPath,
|
||||
pathToLabel,
|
||||
readUiConditionRule,
|
||||
resolveNodeIcon,
|
||||
resolveSchemaRef,
|
||||
resolveSchemaPath,
|
||||
schemaFieldDescription,
|
||||
schemaFieldLabel,
|
||||
shouldSkipSchemaField,
|
||||
splitTemplatedTextParts,
|
||||
toStringOrNull,
|
||||
validateUniqueByConstraint,
|
||||
valueToDisplayString
|
||||
} from '../node-utility';
|
||||
import { evaluateUiConditionRule, flattenPrimitiveValues, formatNodeTitle, getOutputPillClass, getOutputsTitle, getValueByPath, isConditionalByPorts, isHumanInteractiveNode, orderedSchemaPropertyEntries, parentGroupLabel, parentPath, pathToLabel, readUiConditionRule, resolveNodeIcon, resolveSchemaPath, resolveSchemaRef, schemaFieldDescription, schemaFieldLabel, shouldSkipSchemaField, splitTemplatedTextParts, toStringOrNull, type UiConditionRule, validateUniqueByConstraint, valueToDisplayString } from '../node-utility';
|
||||
import {
|
||||
buildSchemaEditableFieldDefinitions,
|
||||
buildSchemaFieldViewModel,
|
||||
|
|
@ -1138,7 +1114,7 @@ export class GenericNodeComponent implements OnDestroy {
|
|||
getFieldValue: (definition, nextConfig) => this.fieldDisplayValue(definition, this.getByPath(nextConfig, definition.path)),
|
||||
isFieldWide: (definition) => this.shouldRenderWideField(definition.label, definition.ui.widget === 'textarea'),
|
||||
getRichContentParts: (path, _nextConfig) => this.toRichContentParts(path),
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.blockSchema, path).group ?? parentPath(path),
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.blockSchema, path).group ?? parentGroupLabel(path),
|
||||
groupRichContent: false
|
||||
});
|
||||
const allFields = [
|
||||
|
|
@ -1156,7 +1132,7 @@ export class GenericNodeComponent implements OnDestroy {
|
|||
fields: allFields,
|
||||
richContentFields: allRichContentFields,
|
||||
arrayFields: this.arrayFields,
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.blockSchema, path).group ?? parentPath(path)
|
||||
resolveGroupLabel: (path) => getSchemaPathUiMeta(this.blockSchema, path).group ?? parentGroupLabel(path)
|
||||
});
|
||||
this.parameterDisplayItems = ordered.rootItems;
|
||||
this.parameterFieldGroups = ordered.groups;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
evaluateUiConditionRule,
|
||||
flattenPrimitiveValues,
|
||||
orderedSchemaPropertyEntries,
|
||||
parentGroupLabel,
|
||||
parentPath,
|
||||
pathToLabel,
|
||||
readUiConditionRule,
|
||||
|
|
@ -222,4 +223,16 @@ describe('node-utility', () => {
|
|||
{ text: '!', isDynamicInput: false }
|
||||
]);
|
||||
});
|
||||
|
||||
it('parentGroupLabel names a nested group readably, not as a dotted path', () => {
|
||||
// Four call sites used parentPath directly, so a field under llmDescriptor.parameters gave a
|
||||
// fieldset legend reading "LLMDESCRIPTOR.PARAMETERS" once the CSS uppercased it.
|
||||
expect(parentGroupLabel('llmDescriptor.parameters.temperature')).toBe('Parameters');
|
||||
expect(parentGroupLabel('llmDescriptor.provider')).toBe('Llm Descriptor');
|
||||
});
|
||||
|
||||
it('parentGroupLabel gives a root-level field no group at all', () => {
|
||||
expect(parentGroupLabel('prompt')).toBeNull();
|
||||
expect(parentGroupLabel('')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -78,6 +78,20 @@ export function parentPath(path: string): string | null {
|
|||
return path.slice(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* The fieldset legend for a field that belongs to a nested object but whose object declared no
|
||||
* name of its own.
|
||||
*
|
||||
* It is the readable form, not the raw path: four call sites used `parentPath` directly, so a
|
||||
* temperature under `llmDescriptor.parameters` produced a legend reading
|
||||
* "LLMDESCRIPTOR.PARAMETERS". One definition, because four copies of the same expression is how
|
||||
* that came to be wrong in four places at once.
|
||||
*/
|
||||
export function parentGroupLabel(path: string): string | null {
|
||||
const parent = parentPath(path);
|
||||
return parent ? pathToLabel(parent) : null;
|
||||
}
|
||||
|
||||
export function resolveSchemaRef(node: Record<string, any>, root: Record<string, any>) {
|
||||
if (!node || typeof node !== 'object') return node;
|
||||
const ref = node['$ref'];
|
||||
|
|
|
|||
|
|
@ -1,18 +1,4 @@
|
|||
import {
|
||||
orderedSchemaPropertyEntries,
|
||||
type UiConditionRule,
|
||||
evaluateUiConditionRule,
|
||||
getValueByPath,
|
||||
parentPath,
|
||||
readEffectiveUiVisibleConditionRule,
|
||||
readUiConditionRule,
|
||||
readUiGroup,
|
||||
readUiLabel,
|
||||
resolveSchemaRef,
|
||||
resolveSchemaPath,
|
||||
schemaFieldLabel,
|
||||
schemaFieldDescription
|
||||
} from './node-utility';
|
||||
import { evaluateUiConditionRule, getValueByPath, orderedSchemaPropertyEntries, parentGroupLabel, readEffectiveUiVisibleConditionRule, readUiConditionRule, readUiGroup, readUiLabel, resolveSchemaPath, resolveSchemaRef, schemaFieldDescription, schemaFieldLabel, type UiConditionRule } from './node-utility';
|
||||
|
||||
export type SchemaFieldType = 'string' | 'number' | 'integer' | 'boolean' | 'unknown';
|
||||
|
||||
|
|
@ -772,7 +758,7 @@ export function buildSchemaFieldViewModel<
|
|||
const grouped = groupSchemaFields({
|
||||
fields: parameterFields,
|
||||
richContentFields: params.groupRichContent ? richContentFields : undefined,
|
||||
resolveGroupLabel: (path) => params.resolveGroupLabel?.(path) ?? parentPath(path),
|
||||
resolveGroupLabel: (path) => params.resolveGroupLabel?.(path) ?? parentGroupLabel(path),
|
||||
resolveLegend: params.resolveLegend
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -26,31 +26,7 @@ import {
|
|||
normalizeFlowDataValue,
|
||||
type SchemaFlowDataFieldDefinition
|
||||
} from '../flow-data-schema-fields';
|
||||
import {
|
||||
type UiConditionRule,
|
||||
evaluateUiConditionRule,
|
||||
flattenPrimitiveValues,
|
||||
formatNodeTitle,
|
||||
getOutputPillClass,
|
||||
getOutputsTitle,
|
||||
isConditionalByPorts,
|
||||
isHumanInteractiveNode,
|
||||
orderedSchemaPropertyEntries,
|
||||
parentPath,
|
||||
pathToLabel,
|
||||
readUiConditionRule,
|
||||
readEffectiveUiVisibleConditionRule,
|
||||
readUiGroup,
|
||||
readUiLabel,
|
||||
resolveNodeIcon,
|
||||
resolveSchemaRef,
|
||||
resolveSchemaPath,
|
||||
schemaFieldLabel,
|
||||
shouldSkipSchemaField,
|
||||
splitTemplatedTextParts,
|
||||
toStringOrNull,
|
||||
valueToDisplayString
|
||||
} from '../node-utility';
|
||||
import { evaluateUiConditionRule, flattenPrimitiveValues, formatNodeTitle, getOutputPillClass, getOutputsTitle, isConditionalByPorts, isHumanInteractiveNode, orderedSchemaPropertyEntries, parentGroupLabel, pathToLabel, readEffectiveUiVisibleConditionRule, readUiConditionRule, readUiGroup, readUiLabel, resolveNodeIcon, resolveSchemaPath, resolveSchemaRef, schemaFieldLabel, shouldSkipSchemaField, splitTemplatedTextParts, toStringOrNull, type UiConditionRule, valueToDisplayString } from '../node-utility';
|
||||
|
||||
type DisplayField = {
|
||||
path: string;
|
||||
|
|
@ -1374,7 +1350,7 @@ export class TaskStepNodeComponent {
|
|||
}
|
||||
|
||||
private groupLabelForPath(path: string): string | null {
|
||||
return this.getFieldUiMeta(path).group ?? parentPath(path);
|
||||
return this.getFieldUiMeta(path).group ?? parentGroupLabel(path);
|
||||
}
|
||||
|
||||
private resolveFieldSchema(path: string): Record<string, any> | null {
|
||||
|
|
|
|||
Loading…
Reference in New Issue