From 7dcba7fb6faa4bfcb561760afd0fae63083fa21c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 27 Nov 2024 17:42:41 +0100 Subject: [PATCH 1/6] Keep hidden fields in the tree --- .../computeRootForm/computeRootForm.test.ts | 36 ++++-- .../computeRootFormFieldGroup.test.ts | 79 +++++++++--- .../computeRootFormFieldGroup.ts | 114 ++++++++++-------- .../mergeRangeSliders.test.ts | 27 +++-- .../mergeTemporaryRangeSliders.test.ts | 9 +- .../mergeTemporaryRangeSliders.ts | 3 +- .../mergeRangeSliders/temporaryRangeSlider.ts | 4 +- .../launcher/decoupledLogic/formTypes.ts | 2 + .../findInRootForm.test.ts | 33 +++-- .../mutateHelmValues_update.test.ts | 12 +- .../launcher/RootFormComponent/Accordion.tsx | 11 +- .../ConfigurationTopLevelGroup.tsx | 26 +++- .../FormFieldGroupComponent.stories.tsx | 9 +- .../FormFieldGroupComponent.tsx | 67 ++++++---- .../formFields/CheckboxFormField.stories.tsx | 1 + .../formFields/CheckboxFormField.tsx | 14 ++- .../formFields/NumberFormField.stories.tsx | 1 + .../formFields/NumberFormField.tsx | 5 +- .../RangeSliderFormField.stories.tsx | 1 + .../formFields/RangeSliderFormField.tsx | 13 +- .../formFields/SelectFormField.stories.tsx | 1 + .../formFields/SelectFormField.tsx | 5 +- .../formFields/SliderFormField.stories.tsx | 1 + .../formFields/SliderFormField.tsx | 5 +- .../formFields/TextFormField.stories.tsx | 1 + .../formFields/TextFormField.tsx | 5 +- .../YamlCodeBlockFormField.stories.tsx | 1 + .../formFields/YamlCodeBlockFormField.tsx | 5 +- .../formFields/shared/FormFieldWrapper.tsx | 20 ++- 29 files changed, 349 insertions(+), 162 deletions(-) diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootForm.test.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootForm.test.ts index 475bc8b62..285f4dcd4 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootForm.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootForm.test.ts @@ -79,11 +79,13 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "foo" + value: "foo", + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false } ], disabledDependencies: [], @@ -97,7 +99,8 @@ describe(symToStr({ computeRootForm }), () => { description: undefined, value: 2, isInteger: false, - minimum: undefined + minimum: undefined, + isHidden: false } ], dependencies: { @@ -110,7 +113,8 @@ describe(symToStr({ computeRootForm }), () => { fieldType: "checkbox", helmValuesPath: ["postgresql", "enabled"], description: undefined, - value: true + value: true, + isHidden: false } ], global: [ @@ -124,7 +128,8 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "admin" + value: "admin", + isHidden: false }, { type: "field", @@ -136,7 +141,8 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "xxx" + value: "xxx", + isHidden: false } ] } @@ -221,11 +227,13 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of services.a" + value: "value of services.a", + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }, { type: "field", @@ -237,7 +245,8 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of foo.b" + value: "value of foo.b", + isHidden: false } ], disabledDependencies: [], @@ -251,7 +260,8 @@ describe(symToStr({ computeRootForm }), () => { description: undefined, value: 2, isInteger: false, - minimum: undefined + minimum: undefined, + isHidden: false }, { type: "field", @@ -263,7 +273,8 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of global.foo.username" + value: "value of global.foo.username", + isHidden: false }, { type: "field", @@ -275,7 +286,8 @@ describe(symToStr({ computeRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of global.foo.password" + value: "value of global.foo.password", + isHidden: false } ], dependencies: {} diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.test.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.test.ts index ec30ad8ad..e7bfed1d9 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.test.ts @@ -47,7 +47,8 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { helmValuesPath: ["a"], doRenderAsTextArea: false, isSensitive: false, - value: "foo" + value: "foo", + isHidden: false }, { type: "field", @@ -57,11 +58,13 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { fieldType: "select", helmValuesPath: ["b"], options: [1, 2, 3], - selectedOptionIndex: 1 + selectedOptionIndex: 1, + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }; expect(got).toStrictEqual(expected); @@ -100,11 +103,13 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { helmValuesPath: ["a"], doRenderAsTextArea: false, isSensitive: false, - value: "foo" + value: "foo", + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }; expect(got).toStrictEqual(expected); @@ -172,15 +177,32 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { fieldType: "checkbox", helmValuesPath: ["persistence", "enabled"], description: "Create a persistent volume", - value: false + value: false, + isHidden: false + }, + { + type: "field", + title: "Persistent volume size", + isReadonly: false, + fieldType: "slider", + helmValuesPath: ["persistence", "size"], + description: "Size of the persistent volume", + min: 1, + max: 100, + unit: "Gi", + step: 1, + value: 10, + isHidden: true } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }; expect(got).toStrictEqual(expected); @@ -248,15 +270,32 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { fieldType: "checkbox", helmValuesPath: ["persistence", "enabled"], description: "Create a persistent volume", - value: false + value: false, + isHidden: false + }, + { + type: "field", + title: "Persistent volume size", + isReadonly: false, + fieldType: "slider", + helmValuesPath: ["persistence", "size"], + description: "Size of the persistent volume", + min: 1, + max: 100, + unit: "Gi", + step: 1, + value: 10, + isHidden: true } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }; expect(got).toStrictEqual(expected); @@ -308,7 +347,8 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of a" + value: "value of a", + isHidden: false }, { type: "group", @@ -332,7 +372,8 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value of foo" + value: "value of foo", + isHidden: false }, { type: "field", @@ -343,19 +384,23 @@ describe(symToStr({ computeRootFormFieldGroup }), () => { description: undefined, value: 42, isInteger: false, - minimum: undefined + minimum: undefined, + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false } ], canAdd: true, - canRemove: false + canRemove: false, + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }; expect(got).toStrictEqual(expected); diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts index 9e4e97c53..aef13f358 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts @@ -9,7 +9,6 @@ import { type XOnyxiaParams } from "core/ports/OnyxiaApi/XOnyxia"; import { getValueAtPathInObject } from "core/tools/getValueAtPathInObject"; -import { exclude } from "tsafe/exclude"; import { same } from "evt/tools/inDepth/same"; import { isAmong } from "tsafe/isAmong"; import type { XOnyxiaContext } from "core/ports/OnyxiaApi"; @@ -74,7 +73,6 @@ export function computeRootFormFieldGroup(params: { helmValuesPath: [] }); - assert(formFieldGroup !== undefined); assert(formFieldGroup.type === "group"); return formFieldGroup; @@ -85,47 +83,51 @@ function computeRootFormFieldGroup_rec(params: { helmValues: Stringifyable; xOnyxiaContext: XOnyxiaContextLike; helmValuesPath: (string | number)[]; -}): FormFieldGroup | FormField | undefined { +}): FormFieldGroup | FormField { const { helmValuesSchema, helmValues, xOnyxiaContext, helmValuesPath } = params; - root_hidden: { - const { hidden } = helmValuesSchema; + const isHidden: boolean = (() => { + root_hidden: { + const { hidden } = helmValuesSchema; - if (hidden === undefined) { - break root_hidden; - } + if (hidden === undefined) { + break root_hidden; + } - if (hidden === false) { - break root_hidden; - } + if (hidden === false) { + break root_hidden; + } - if (hidden === true) { - return undefined; - } + if (hidden === true) { + return true; + } - const { value, path, isPathRelative = false } = hidden; + const { value, path, isPathRelative = false } = hidden; - const splittedPath = path.split("/"); + const splittedPath = path.split("/"); - const helmValuesPath_target = isPathRelative - ? [...helmValuesPath.slice(0, -1), ...splittedPath] - : splittedPath; + const helmValuesPath_target = isPathRelative + ? [...helmValuesPath.slice(0, -1), ...splittedPath] + : splittedPath; - const value_target = getValueAtPathInObject({ - obj: helmValues, - path: helmValuesPath_target - }); + const value_target = getValueAtPathInObject({ + obj: helmValues, + path: helmValuesPath_target + }); - if (!same(value, value_target)) { - break root_hidden; + if (!same(value, value_target)) { + break root_hidden; + } + + return true; } - return undefined; - } + if (helmValuesSchema["x-onyxia"]?.hidden === true) { + return true; + } - if (helmValuesSchema["x-onyxia"]?.hidden === true) { - return undefined; - } + return false; + })(); const title = (() => { const { title } = helmValuesSchema; @@ -200,7 +202,8 @@ function computeRootFormFieldGroup_rec(params: { assert(value instanceof Object); return value; - })() + })(), + isHidden }); } @@ -232,7 +235,8 @@ function computeRootFormFieldGroup_rec(params: { assert(selectedOptionIndex !== -1); return selectedOptionIndex; - })() + })(), + isHidden }); } @@ -290,7 +294,8 @@ function computeRootFormFieldGroup_rec(params: { } assert(false); - })() + })(), + isHidden }); } @@ -337,6 +342,7 @@ function computeRootFormFieldGroup_rec(params: { })(), helmValuesPath, description: helmValuesSchema.description, + isHidden, ...(() => { switch (sliderExtremity) { case "down": @@ -359,18 +365,18 @@ function computeRootFormFieldGroup_rec(params: { helmValuesPath, title, description: helmValuesSchema.description, - nodes: Object.entries(helmValuesSchema.properties) - .map(([segment, helmValuesSchema_child]) => + nodes: Object.entries(helmValuesSchema.properties).map( + ([segment, helmValuesSchema_child]) => computeRootFormFieldGroup_rec({ helmValues, helmValuesPath: [...helmValuesPath, segment], xOnyxiaContext, helmValuesSchema: helmValuesSchema_child }) - ) - .filter(exclude(undefined)), + ), canAdd: false, - canRemove: false + canRemove: false, + isHidden }); case "array": { const itemSchema = helmValuesSchema.items; @@ -390,18 +396,17 @@ function computeRootFormFieldGroup_rec(params: { helmValuesPath, title, description: helmValuesSchema.description, - nodes: values - .map((...[, index]) => - computeRootFormFieldGroup_rec({ - helmValues, - helmValuesPath: [...helmValuesPath, index], - xOnyxiaContext, - helmValuesSchema: itemSchema - }) - ) - .filter(exclude(undefined)), + nodes: values.map((...[, index]) => + computeRootFormFieldGroup_rec({ + helmValues, + helmValuesPath: [...helmValuesPath, index], + xOnyxiaContext, + helmValuesSchema: itemSchema + }) + ), canAdd: values.length < (helmValuesSchema.maxItems ?? Infinity), - canRemove: values.length > (helmValuesSchema.minItems ?? 0) + canRemove: values.length > (helmValuesSchema.minItems ?? 0), + isHidden }); } case "boolean": @@ -418,7 +423,8 @@ function computeRootFormFieldGroup_rec(params: { assert(typeof value === "boolean"); return value; - })() + })(), + isHidden }); case "string": return id({ @@ -437,7 +443,8 @@ function computeRootFormFieldGroup_rec(params: { assert(typeof value === "string"); return value; - })() + })(), + isHidden }); case "integer": case "number": @@ -456,9 +463,10 @@ function computeRootFormFieldGroup_rec(params: { return value; })(), isInteger: helmValuesSchemaType === "integer", - minimum: helmValuesSchema.minimum + minimum: helmValuesSchema.minimum, + isHidden }); } - assert>(false); + assert>; } diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeRangeSliders.test.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeRangeSliders.test.ts index 01d0c0d59..6fbfea40f 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeRangeSliders.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeRangeSliders.test.ts @@ -32,7 +32,8 @@ describe(symToStr({ mergeRangeSliders }), () => { helmValuesPath: ["resources", "requests", "cpu"], description: "The amount of cpu guaranteed", sliderExtremity: "down", - title: "CPU" + title: "CPU", + isHidden: false } }) ], @@ -54,7 +55,8 @@ describe(symToStr({ mergeRangeSliders }), () => { helmValue: "30000m", helmValuesPath: ["resources", "limits", "cpu"], description: "The maximum amount of cpu", - sliderExtremity: "up" + sliderExtremity: "up", + isHidden: false } }) ], @@ -100,7 +102,8 @@ describe(symToStr({ mergeRangeSliders }), () => { min: 50, max: 40000, description: "The maximum amount of cpu" - } + }, + isHidden: false }) ], canAdd: false @@ -136,7 +139,8 @@ describe(symToStr({ mergeRangeSliders }), () => { helmValuesPath: ["resources", "requests", "cpu"], description: "The amount of cpu guaranteed", sliderExtremity: "down", - title: "CPU" + title: "CPU", + isHidden: false } }), createTemporaryRangeSlider({ @@ -156,7 +160,8 @@ describe(symToStr({ mergeRangeSliders }), () => { ], description: "The amount of memory guaranteed", sliderExtremity: "down", - title: "memory" + title: "memory", + isHidden: false } }) ], @@ -178,7 +183,8 @@ describe(symToStr({ mergeRangeSliders }), () => { helmValue: "30000m", helmValuesPath: ["resources", "limits", "cpu"], description: "The maximum amount of cpu", - sliderExtremity: "up" + sliderExtremity: "up", + isHidden: false } }), createTemporaryRangeSlider({ @@ -193,7 +199,8 @@ describe(symToStr({ mergeRangeSliders }), () => { helmValue: "50Gi", helmValuesPath: ["resources", "limits", "memory"], description: "The maximum amount of memory", - sliderExtremity: "up" + sliderExtremity: "up", + isHidden: false } }) ], @@ -239,7 +246,8 @@ describe(symToStr({ mergeRangeSliders }), () => { min: 50, max: 40000, description: "The maximum amount of cpu" - } + }, + isHidden: false }), id({ type: "field", @@ -264,7 +272,8 @@ describe(symToStr({ mergeRangeSliders }), () => { min: 1, max: 200, description: "The maximum amount of memory" - } + }, + isHidden: false }) ], canAdd: false diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.test.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.test.ts index 0630ee64a..276218e6d 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.test.ts @@ -29,7 +29,8 @@ describe(symToStr({ mergeTemporaryRangeSliders }), () => { min: 50, max: 40000, description: "The maximum amount of cpu" - } + }, + isHidden: false }; const got = mergeTemporaryRangeSliders({ @@ -46,7 +47,8 @@ describe(symToStr({ mergeTemporaryRangeSliders }), () => { helmValuesPath: ["resources", "requests", "cpu"], description: "The amount of cpu guaranteed", sliderExtremity: "down", - title: "CPU" + title: "CPU", + isHidden: false } }), temporaryRangeSlider_higherBound: createTemporaryRangeSlider({ @@ -61,7 +63,8 @@ describe(symToStr({ mergeTemporaryRangeSliders }), () => { helmValue: "30000m", helmValuesPath: ["resources", "limits", "cpu"], description: "The maximum amount of cpu", - sliderExtremity: "up" + sliderExtremity: "up", + isHidden: false } }) }); diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.ts index 37a82868a..26568a98a 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/mergeTemporaryRangeSliders.ts @@ -130,6 +130,7 @@ export function mergeTemporaryRangeSliders(params: { lowEndRange, highEndRange }; - })() + })(), + isHidden: payload_lowEnd.isHidden || payload_highEnd.isHidden }; } diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/temporaryRangeSlider.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/temporaryRangeSlider.ts index 57b627618..c01286a9f 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/temporaryRangeSlider.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/mergeRangeSliders/temporaryRangeSlider.ts @@ -12,6 +12,7 @@ export type TemporaryRangeSliderPayload = { helmValue: string | number; helmValuesPath: (string | number)[]; description: string | undefined; + isHidden: boolean; } & ( | { sliderExtremity: "down"; @@ -71,6 +72,7 @@ export function createTemporaryRangeSlider(params: { max: NaN }, unit: prefix + JSON.stringify(payload), - step: NaN + step: NaN, + isHidden: false }; } diff --git a/web/src/core/usecases/launcher/decoupledLogic/formTypes.ts b/web/src/core/usecases/launcher/decoupledLogic/formTypes.ts index 98997bd73..83ef6917f 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/formTypes.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/formTypes.ts @@ -23,6 +23,7 @@ export type FormFieldGroup = { nodes: (FormField | FormFieldGroup)[]; canAdd: boolean; canRemove: boolean; + isHidden: boolean; }; export type FormField = @@ -38,6 +39,7 @@ export namespace FormField { type Common = { type: "field"; title: string; + isHidden: boolean; }; export type Checkbox = Common & { diff --git a/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/findInRootForm.test.ts b/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/findInRootForm.test.ts index 32f0c4b07..7063e056e 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/findInRootForm.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/findInRootForm.test.ts @@ -21,11 +21,13 @@ const rootForm: RootForm = { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "foo" + value: "foo", + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false }, { type: "group", @@ -56,11 +58,13 @@ const rootForm: RootForm = { }, step: undefined, unit: "Mi", - title: "cpu" + title: "cpu", + isHidden: false } ], canAdd: false, - canRemove: false + canRemove: false, + isHidden: false } ], disabledDependencies: [], @@ -74,7 +78,8 @@ const rootForm: RootForm = { description: undefined, value: 2, isInteger: false, - minimum: undefined + minimum: undefined, + isHidden: false } ], dependencies: { @@ -87,7 +92,8 @@ const rootForm: RootForm = { fieldType: "checkbox", helmValuesPath: ["postgresql", "enabled"], description: undefined, - value: true + value: true, + isHidden: false } ], global: [ @@ -101,7 +107,8 @@ const rootForm: RootForm = { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "admin" + value: "admin", + isHidden: false }, { type: "field", @@ -113,7 +120,8 @@ const rootForm: RootForm = { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "xxx" + value: "xxx", + isHidden: false } ] } @@ -137,7 +145,8 @@ describe(symToStr({ findInRootForm }), () => { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "foo" + value: "foo", + isHidden: false }); }); @@ -154,7 +163,8 @@ describe(symToStr({ findInRootForm }), () => { fieldType: "checkbox", helmValuesPath: ["postgresql", "enabled"], description: undefined, - value: true + value: true, + isHidden: false }); }); }); @@ -190,7 +200,8 @@ describe(symToStr({ findInRootForm_rangeSlider }), () => { }, step: undefined, unit: "Mi", - title: "cpu" + title: "cpu", + isHidden: false }); }); }); diff --git a/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/mutateHelmValues_update.test.ts b/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/mutateHelmValues_update.test.ts index 9fb9ad10b..68b216c2a 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/mutateHelmValues_update.test.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/mutateHelmValues/mutateHelmValues_update/mutateHelmValues_update.test.ts @@ -42,7 +42,8 @@ describe(symToStr({ mutateHelmValues_update }), () => { isReadonly: THROW_IF_ACCESSED, description: THROW_IF_ACCESSED, selectedOptionIndex: THROW_IF_ACCESSED, - title: THROW_IF_ACCESSED + title: THROW_IF_ACCESSED, + isHidden: THROW_IF_ACCESSED }) ], dependencies: {}, @@ -79,7 +80,8 @@ describe(symToStr({ mutateHelmValues_update }), () => { min: THROW_IF_ACCESSED, max: THROW_IF_ACCESSED, step: THROW_IF_ACCESSED, - value: THROW_IF_ACCESSED + value: THROW_IF_ACCESSED, + isHidden: THROW_IF_ACCESSED }) ], dependencies: {}, @@ -160,10 +162,12 @@ describe(symToStr({ mutateHelmValues_update }), () => { } ), step: THROW_IF_ACCESSED, - title: THROW_IF_ACCESSED + title: THROW_IF_ACCESSED, + isHidden: THROW_IF_ACCESSED } ) - ] + ], + isHidden: THROW_IF_ACCESSED }) ], dependencies: {}, diff --git a/web/src/ui/pages/launcher/RootFormComponent/Accordion.tsx b/web/src/ui/pages/launcher/RootFormComponent/Accordion.tsx index f3e97a4a7..d4868b740 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/Accordion.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/Accordion.tsx @@ -26,6 +26,7 @@ export type Props = { canAdd: boolean; canRemove: boolean; callbacks: FormCallbacks; + isHidden: boolean; }; export function Accordion(props: Props) { @@ -37,10 +38,11 @@ export function Accordion(props: Props) { nodes, canAdd, canRemove, - callbacks + callbacks, + isHidden } = props; - const { classes, cx } = useStyles(); + const { classes, cx } = useStyles({ isHidden }); const contentId = useId(); @@ -148,6 +150,7 @@ export function Accordion(props: Props) { canAdd={canAdd} canRemove={canRemove} callbacks={callbacks} + isHidden={false} /> @@ -157,8 +160,10 @@ export function Accordion(props: Props) { const useStyles = tss .withName({ Accordion }) .withNestedSelectors<"summary" | "summaryExpanded">() - .create(({ theme, classes }) => ({ + .withParams<{ isHidden: boolean }>() + .create(({ theme, isHidden, classes }) => ({ root: { + display: isHidden ? "none" : undefined, backgroundColor: theme.colors.useCases.surfaces.surface1 }, summaryExpanded: {}, diff --git a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx index 2c0295d9c..32c46d761 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx @@ -57,7 +57,8 @@ export function ConfigurationTopLevelGroup(props: Props) { description: "configuration that applies to all charts", canAdd: false, canRemove: false, - nodes: global + nodes: global, + isHidden: false }) ]), ...(main_formFields.length === 0 @@ -71,7 +72,8 @@ export function ConfigurationTopLevelGroup(props: Props) { description: "Top level configuration values", canAdd: false, canRemove: false, - nodes: main_formFields + nodes: main_formFields, + isHidden: false }) ]), ...main_formFieldGroups.map(node => { @@ -92,9 +94,11 @@ export function ConfigurationTopLevelGroup(props: Props) { helmValuesPath: node.helmValuesPath, isReadonly: node.isReadonly, title: "", - value: node.value + value: node.value, + isHidden: false }) - ] + ], + isHidden: node.isHidden }); } @@ -104,7 +108,8 @@ export function ConfigurationTopLevelGroup(props: Props) { description: node.description, canAdd: node.canAdd, canRemove: node.canRemove, - nodes: node.nodes + nodes: node.nodes, + isHidden: node.isHidden }); }) ]; @@ -115,7 +120,15 @@ export function ConfigurationTopLevelGroup(props: Props) { return (
{accordionEntries.map( - ({ helmValuesPath, title, description, canAdd, canRemove, nodes }) => ( + ({ + helmValuesPath, + title, + description, + canAdd, + canRemove, + nodes, + isHidden + }) => ( ) )} diff --git a/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.stories.tsx b/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.stories.tsx index b03085860..7d1a9b578 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.stories.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.stories.tsx @@ -36,7 +36,8 @@ export const Default: Story = { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value 1" + value: "value 1", + isHidden: false }), id({ type: "field", @@ -48,7 +49,8 @@ export const Default: Story = { doRenderAsTextArea: false, isSensitive: false, pattern: undefined, - value: "value 2" + value: "value 2", + isHidden: false }) ], callbacks: { @@ -56,6 +58,7 @@ export const Default: Story = { onAdd, onRemove, onFieldErrorChange - } + }, + isHidden: false } }; diff --git a/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.tsx b/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.tsx index 18dfdb2ff..01c4d3c5d 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/FormFieldGroupComponent/FormFieldGroupComponent.tsx @@ -25,10 +25,12 @@ export type Props = { canAdd: boolean; canRemove: boolean; callbacks: FormCallbacks; + isHidden: boolean; }; export function FormFieldGroupComponent(props: Props) { - const { className, canAdd, canRemove, nodes, callbacks, helmValuesPath } = props; + const { className, canAdd, canRemove, nodes, callbacks, helmValuesPath, isHidden } = + props; const { onRemove, onAdd, onChange, onFieldErrorChange } = callbacks; @@ -124,7 +126,7 @@ export function FormFieldGroupComponent(props: Props) { }) ); - const { cx, classes } = useStyles_inner(); + const { cx, classes } = useStyles({ isHidden }); const { t } = useTranslation({ FormFieldGroupComponent }); @@ -157,6 +159,7 @@ export function FormFieldGroupComponent(props: Props) { canAdd={node.canAdd} canRemove={node.canRemove} helmValuesPath={node.helmValuesPath} + isHidden={node.isHidden} /> ); @@ -175,6 +178,7 @@ export function FormFieldGroupComponent(props: Props) { onChange={getOnChange_checkbox( JSON.stringify(node.helmValuesPath) )} + isHidden={node.isHidden} /> ); case "yaml code block": { @@ -192,6 +196,7 @@ export function FormFieldGroupComponent(props: Props) { onErrorChange={getOnFieldErrorChange_child( helmValuesPathStr )} + isHidden={node.isHidden} /> ); } @@ -211,6 +216,7 @@ export function FormFieldGroupComponent(props: Props) { onErrorChange={getOnFieldErrorChange_child( helmValuesPathStr )} + isHidden={node.isHidden} /> ); } @@ -227,6 +233,7 @@ export function FormFieldGroupComponent(props: Props) { onSelectedOptionIndexChange={getOnChange_select( JSON.stringify(node.helmValuesPath) )} + isHidden={node.isHidden} /> ); case "text field": { @@ -247,6 +254,7 @@ export function FormFieldGroupComponent(props: Props) { onErrorChange={getOnFieldErrorChange_child( helmValuesPathStr )} + isHidden={node.isHidden} /> ); } @@ -267,6 +275,7 @@ export function FormFieldGroupComponent(props: Props) { onChange={getOnChange_slider( JSON.stringify(node.helmValuesPath) )} + isHidden={node.isHidden} /> ); case "range slider": @@ -297,6 +306,7 @@ export function FormFieldGroupComponent(props: Props) { JSON.stringify(node.lowEndRange.helmValuesPath), JSON.stringify(node.highEndRange.helmValuesPath) )} + isHidden={node.isHidden} /> ); } @@ -317,28 +327,33 @@ export function FormFieldGroupComponent(props: Props) { const { i18n } = declareComponentKeys<"add">()({ FormFieldGroupComponent }); export type I18n = typeof i18n; -const useStyles_inner = tss.withName({ FormFieldGroupComponent }).create(({ theme }) => { - const gap = theme.spacing(6); +const useStyles = tss + .withName({ FormFieldGroupComponent }) + .withParams<{ isHidden: boolean }>() + .create(({ theme, isHidden }) => { + const gap = theme.spacing(6); - return { - root: { - display: "flex", - flexWrap: "wrap", - gap, - alignItems: "baseline" - }, - group: { - flex: "0 0 100%" - }, - field_text: { - flex: "0 0 300px" - }, - field_yamlCodeBlock: { - flex: "0 0 100%" - }, - field_slider: { - flex: `0 0 calc(50% - ${gap / 2}px)`, - boxSizing: "border-box" - } - }; -}); + return { + root: isHidden + ? { display: "none" } + : { + display: "flex", + flexWrap: "wrap", + gap, + alignItems: "baseline" + }, + group: { + flex: "0 0 100%" + }, + field_text: { + flex: "0 0 300px" + }, + field_yamlCodeBlock: { + flex: "0 0 100%" + }, + field_slider: { + flex: `0 0 calc(50% - ${gap / 2}px)`, + boxSizing: "border-box" + } + }; + }); diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.stories.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.stories.tsx index 7bcdab24f..f806f9a82 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.stories.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.stories.tsx @@ -38,6 +38,7 @@ function StoryWrapper(params: Params) { setValue(newValue); }} onRemove={undefined} + isHidden={false} />

Value:

diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.tsx index 52620932e..08d7a8632 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/CheckboxFormField.tsx @@ -12,11 +12,20 @@ type Props = { onRemove: (() => void) | undefined; value: boolean; onChange: (newValue: boolean) => void; + isHidden: boolean; }; export const CheckboxFormField = memo((props: Props) => { - const { className, title, description, isReadonly, onRemove, value, onChange } = - props; + const { + className, + title, + description, + isReadonly, + onRemove, + value, + onChange, + isHidden + } = props; const { serializedValue, setSerializedValue, resetToDefault } = useFormField< boolean, @@ -45,6 +54,7 @@ export const CheckboxFormField = memo((props: Props) => { onResetToDefault={resetToDefault} inputId={inputId} onRemove={onRemove} + isHidden={isHidden} >

Value:

diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/NumberFormField.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/NumberFormField.tsx index dd4006ae6..6492d1ca0 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/NumberFormField.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/NumberFormField.tsx @@ -18,6 +18,7 @@ type Props = { value: number; onChange: (newValue: number) => void; onErrorChange: (params: { hasError: boolean }) => void; + isHidden: boolean; }; export const NumberFormField = memo((props: Props) => { @@ -31,7 +32,8 @@ export const NumberFormField = memo((props: Props) => { onRemove, value, onChange, - onErrorChange + onErrorChange, + isHidden } = props; const { serializedValue, setSerializedValue, errorMessageKey, resetToDefault } = @@ -114,6 +116,7 @@ export const NumberFormField = memo((props: Props) => { onResetToDefault={resetToDefault} inputId={inputId} onRemove={onRemove} + isHidden={isHidden} >

Value:

diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/RangeSliderFormField.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/RangeSliderFormField.tsx index a0ce7a7e9..43b1af679 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/RangeSliderFormField.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/RangeSliderFormField.tsx @@ -13,6 +13,7 @@ export type Props = { lowEndRange: Props.RangeEnd; highEndRange: Props.RangeEnd; onChange: (params: { lowEndRangeValue: number; highEndRangeValue: number }) => void; + isHidden: boolean; }; export namespace Props { @@ -27,7 +28,16 @@ export namespace Props { } export const RangeSliderFormField = memo((props: Props) => { - const { className, title, unit, step, lowEndRange, highEndRange, onChange } = props; + const { + className, + title, + unit, + step, + lowEndRange, + highEndRange, + onChange, + isHidden + } = props; const serialize = ([lowEndRangeValue, highEndRangeValue]: [number, number]) => JSON.stringify([lowEndRangeValue, highEndRangeValue]); @@ -70,6 +80,7 @@ export const RangeSliderFormField = memo((props: Props) => { onResetToDefault={resetToDefault} inputId={inputId} onRemove={undefined} + isHidden={isHidden} >

selectedOptionIndex:

diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/SelectFormField.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/SelectFormField.tsx index 6435e12d1..7eadb516f 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/SelectFormField.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/SelectFormField.tsx @@ -15,6 +15,7 @@ type Props = { onRemove: (() => void) | undefined; selectedOptionIndex: number; onSelectedOptionIndexChange: (selectedOptionIndex: number) => void; + isHidden: boolean; }; export const SelectFormField = memo((props: Props) => { @@ -26,7 +27,8 @@ export const SelectFormField = memo((props: Props) => { options, onRemove, selectedOptionIndex, - onSelectedOptionIndexChange + onSelectedOptionIndexChange, + isHidden } = props; const { serializedValue, setSerializedValue, resetToDefault } = useFormField< @@ -56,6 +58,7 @@ export const SelectFormField = memo((props: Props) => { onResetToDefault={resetToDefault} inputId={inputId} onRemove={onRemove} + isHidden={isHidden} >

diff --git a/web/src/ui/pages/launcher/RootFormComponent/formFields/YamlCodeBlockFormField.tsx b/web/src/ui/pages/launcher/RootFormComponent/formFields/YamlCodeBlockFormField.tsx index 493710ab2..87743f6b4 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/formFields/YamlCodeBlockFormField.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/formFields/YamlCodeBlockFormField.tsx @@ -17,6 +17,7 @@ type Props = { value: Record | Stringifyable[]; onChange: (newValue: Record | Stringifyable[]) => void; onErrorChange: (params: { hasError: boolean }) => void; + isHidden: boolean; }; const DEFAULT_HEIGHT = 300; @@ -30,7 +31,8 @@ export const YamlCodeBlockFormField = memo((props: Props) => { onRemove, value, onChange, - onErrorChange + onErrorChange, + isHidden } = props; const { t } = useTranslation({ YamlCodeBlockFormField }); @@ -99,6 +101,7 @@ export const YamlCodeBlockFormField = memo((props: Props) => { onResetToDefault={resetToDefault} inputId={inputId} onRemove={onRemove} + isHidden={isHidden} > void) | undefined; + isHidden: boolean; children: ReactNode; }; @@ -28,18 +29,20 @@ export function FormFieldWrapper(props: Props) { error, inputId, onRemove, + isHidden, children } = props; - const { classes } = useStyles({ + const { cx, classes } = useStyles({ isErrored: error !== undefined, - isResetToDefaultButtonVisible: onResetToDefault !== undefined + isResetToDefaultButtonVisible: onResetToDefault !== undefined, + isHidden }); const { t } = useTranslation({ FormFieldWrapper }); return ( -
+
{onRemove !== undefined && ( () - .create(({ theme, isErrored, isResetToDefaultButtonVisible }) => ({ + .withParams<{ + isErrored: boolean; + isResetToDefaultButtonVisible: boolean; + isHidden: boolean; + }>() + .create(({ theme, isErrored, isResetToDefaultButtonVisible, isHidden }) => ({ + root: { + display: isHidden ? "none" : undefined + }, title: { color: !isErrored ? undefined : theme.colors.useCases.alertSeverity.error.main }, From a3f0cc4e6446f294320a4763237c4062105205b0 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 28 Nov 2024 16:17:21 +0100 Subject: [PATCH 2/6] More carfull check of validity of an object against json schema --- .../validateValueAgainstJSONSchema_noEnumCheck.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts b/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts index bd49f59a0..97c79ba76 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts @@ -355,12 +355,24 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { return { isValid: true }; } + if ( + Object.keys(value).length !== Object.keys(helmValuesSchema.properties).length + ) { + return { isValid: false, reasonableApproximation: undefined }; + } + let isreasonableApproximation = false; const valueOrreasonableApproximation: Record = {}; for (const [key, value_i] of Object.entries(value)) { + const helmValuesSchema_i = helmValuesSchema.properties[key]; + + if (helmValuesSchema_i === undefined) { + return { isValid: false, reasonableApproximation: undefined }; + } + const validationResult = validateValueAgainstJSONSchema({ - helmValuesSchema: helmValuesSchema.properties[key], + helmValuesSchema: helmValuesSchema_i, value: value_i, xOnyxiaContext }); From 4d681a20a101daf2bc77ef992d4f86fb8247c64f Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 28 Nov 2024 16:18:04 +0100 Subject: [PATCH 3/6] Formatting fix --- ...idateValueAgainstJSONSchema_noEnumCheck.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts b/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts index 97c79ba76..56d0390a3 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/shared/validateValueAgainstJSONSchema/validateValueAgainstJSONSchema_noEnumCheck.ts @@ -314,8 +314,8 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { return { isValid: true }; } - let isreasonableApproximation = false; - let valueOrreasonableApproximation: Stringifyable[] = []; + let isReasonableApproximation = false; + let valueOrReasonableApproximation: Stringifyable[] = []; for (const value_i of value) { const validationResult = validateValueAgainstJSONSchema({ @@ -325,7 +325,7 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { }); if (validationResult.isValid) { - valueOrreasonableApproximation.push(value_i); + valueOrReasonableApproximation.push(value_i); continue; } @@ -333,15 +333,15 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { return { isValid: false, reasonableApproximation: undefined }; } - isreasonableApproximation = true; + isReasonableApproximation = true; - valueOrreasonableApproximation.push(validationResult.reasonableApproximation); + valueOrReasonableApproximation.push(validationResult.reasonableApproximation); } - return isreasonableApproximation + return isReasonableApproximation ? { isValid: false, - reasonableApproximation: valueOrreasonableApproximation + reasonableApproximation: valueOrReasonableApproximation } : { isValid: true }; } @@ -361,8 +361,8 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { return { isValid: false, reasonableApproximation: undefined }; } - let isreasonableApproximation = false; - const valueOrreasonableApproximation: Record = {}; + let isReasonableApproximation = false; + const valueOrReasonableApproximation: Record = {}; for (const [key, value_i] of Object.entries(value)) { const helmValuesSchema_i = helmValuesSchema.properties[key]; @@ -378,7 +378,7 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { }); if (validationResult.isValid) { - valueOrreasonableApproximation[key] = value_i; + valueOrReasonableApproximation[key] = value_i; continue; } @@ -386,16 +386,16 @@ export function validateValueAgainstJSONSchema_noEnumCheck(params: { return { isValid: false, reasonableApproximation: undefined }; } - isreasonableApproximation = true; + isReasonableApproximation = true; - valueOrreasonableApproximation[key] = + valueOrReasonableApproximation[key] = validationResult.reasonableApproximation; } - return isreasonableApproximation + return isReasonableApproximation ? { isValid: false, - reasonableApproximation: valueOrreasonableApproximation + reasonableApproximation: valueOrReasonableApproximation } : { isValid: true }; } From ec6c9bce502ff45f8de15ad21ad698dfcd044d44 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 28 Nov 2024 16:25:57 +0100 Subject: [PATCH 4/6] Fix access exception --- .../ConfigurationTopLevelGroup.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx index 32c46d761..72f5183f1 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx @@ -7,7 +7,10 @@ import type { import { Accordion, type Props as PropsOfAccordion } from "./Accordion"; import type { FormCallbacks } from "./FormCallbacks"; import { id } from "tsafe/id"; -import { createObjectThatThrowsIfAccessed } from "clean-architecture/createObjectThatThrowsIfAccessed"; +import { + createObjectThatThrowsIfAccessed, + isObjectThatThrowIfAccessed +} from "clean-architecture/createObjectThatThrowsIfAccessed"; type Props = { className?: string; @@ -130,7 +133,13 @@ export function ConfigurationTopLevelGroup(props: Props) { isHidden }) => ( { + if (isObjectThatThrowIfAccessed(helmValuesPath)) { + return title; + } + + return JSON.stringify(helmValuesPath); + })()} helmValuesPath={helmValuesPath} title={title} description={description} From 86c76a13f465278d05967675d2df745d63ed28fd Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 28 Nov 2024 17:09:34 +0100 Subject: [PATCH 5/6] Hide group if every node is hidden --- .../computeRootFormFieldGroup.ts | 46 +++++++++++-------- .../ConfigurationTopLevelGroup.tsx | 6 +-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts index aef13f358..fb580d25b 100644 --- a/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts +++ b/web/src/core/usecases/launcher/decoupledLogic/computeRootForm/computeRootFormFieldGroup.ts @@ -358,26 +358,30 @@ function computeRootFormFieldGroup_rec(params: { } switch (helmValuesSchemaType) { - case "object": + case "object": { assert(helmValuesSchema.properties !== undefined); + + const nodes = Object.entries(helmValuesSchema.properties).map( + ([segment, helmValuesSchema_child]) => + computeRootFormFieldGroup_rec({ + helmValues, + helmValuesPath: [...helmValuesPath, segment], + xOnyxiaContext, + helmValuesSchema: helmValuesSchema_child + }) + ); + return id({ type: "group", helmValuesPath, title, description: helmValuesSchema.description, - nodes: Object.entries(helmValuesSchema.properties).map( - ([segment, helmValuesSchema_child]) => - computeRootFormFieldGroup_rec({ - helmValues, - helmValuesPath: [...helmValuesPath, segment], - xOnyxiaContext, - helmValuesSchema: helmValuesSchema_child - }) - ), + nodes, canAdd: false, canRemove: false, - isHidden + isHidden: isHidden || nodes.every(node => node.isHidden) }); + } case "array": { const itemSchema = helmValuesSchema.items; @@ -391,22 +395,24 @@ function computeRootFormFieldGroup_rec(params: { assert(values !== undefined); assert(values instanceof Array); + const nodes = values.map((...[, index]) => + computeRootFormFieldGroup_rec({ + helmValues, + helmValuesPath: [...helmValuesPath, index], + xOnyxiaContext, + helmValuesSchema: itemSchema + }) + ); + return id({ type: "group", helmValuesPath, title, description: helmValuesSchema.description, - nodes: values.map((...[, index]) => - computeRootFormFieldGroup_rec({ - helmValues, - helmValuesPath: [...helmValuesPath, index], - xOnyxiaContext, - helmValuesSchema: itemSchema - }) - ), + nodes, canAdd: values.length < (helmValuesSchema.maxItems ?? Infinity), canRemove: values.length > (helmValuesSchema.minItems ?? 0), - isHidden + isHidden: isHidden || nodes.every(node => node.isHidden) }); } case "boolean": diff --git a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx index 72f5183f1..36619246e 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx @@ -61,7 +61,7 @@ export function ConfigurationTopLevelGroup(props: Props) { canAdd: false, canRemove: false, nodes: global, - isHidden: false + isHidden: global.every(node => node.isHidden) }) ]), ...(main_formFields.length === 0 @@ -76,7 +76,7 @@ export function ConfigurationTopLevelGroup(props: Props) { canAdd: false, canRemove: false, nodes: main_formFields, - isHidden: false + isHidden: main_formFields.every(node => node.isHidden) }) ]), ...main_formFieldGroups.map(node => { @@ -98,7 +98,7 @@ export function ConfigurationTopLevelGroup(props: Props) { isReadonly: node.isReadonly, title: "", value: node.value, - isHidden: false + isHidden: node.isHidden }) ], isHidden: node.isHidden From 8a891d750d780bfde7c2e1a593a45a961b19ee71 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 28 Nov 2024 17:59:10 +0100 Subject: [PATCH 6/6] Add missing translations --- web/src/ui/i18n/resources/de.tsx | 6 ++++++ web/src/ui/i18n/resources/en.tsx | 6 ++++++ web/src/ui/i18n/resources/es.tsx | 6 ++++++ web/src/ui/i18n/resources/fi.tsx | 6 ++++++ web/src/ui/i18n/resources/fr.tsx | 6 ++++++ web/src/ui/i18n/resources/it.tsx | 6 ++++++ web/src/ui/i18n/resources/nl.tsx | 6 ++++++ web/src/ui/i18n/resources/no.tsx | 6 ++++++ web/src/ui/i18n/resources/zh-CN.tsx | 5 +++++ web/src/ui/i18n/types.ts | 1 + .../ConfigurationTopLevelGroup.tsx | 20 ++++++++++++++----- 11 files changed, 69 insertions(+), 5 deletions(-) diff --git a/web/src/ui/i18n/resources/de.tsx b/web/src/ui/i18n/resources/de.tsx index 22671b2e2..0d317eeed 100644 --- a/web/src/ui/i18n/resources/de.tsx +++ b/web/src/ui/i18n/resources/de.tsx @@ -679,6 +679,12 @@ Fühlen Sie sich frei, Ihre Kubernetes-Bereitstellungen zu erkunden und die Kont FormFieldWrapper: { "reset to default": "Zurücksetzen auf Standardwert" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Verschiedenes", + "Configuration that applies to all charts": + "Konfiguration, die für alle Diagramme gilt", + "Top level configuration values": "Konfigurationswerte auf oberster Ebene" + }, YamlCodeBlockFormField: { "not an array": "Ein Array wird erwartet", "not an object": "Ein Objekt wird erwartet", diff --git a/web/src/ui/i18n/resources/en.tsx b/web/src/ui/i18n/resources/en.tsx index e6c28a033..249f16018 100644 --- a/web/src/ui/i18n/resources/en.tsx +++ b/web/src/ui/i18n/resources/en.tsx @@ -667,6 +667,12 @@ Feel free to explore and take charge of your Kubernetes deployments! FormFieldWrapper: { "reset to default": "Reset to default" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Miscellaneous", + "Configuration that applies to all charts": + "Configuration that applies to all charts", + "Top level configuration values": "Top level configuration values" + }, YamlCodeBlockFormField: { "not an array": "An array is expected", "not an object": "An object is expected", diff --git a/web/src/ui/i18n/resources/es.tsx b/web/src/ui/i18n/resources/es.tsx index 8a624c46f..fb99247f4 100644 --- a/web/src/ui/i18n/resources/es.tsx +++ b/web/src/ui/i18n/resources/es.tsx @@ -675,6 +675,12 @@ export const translations: Translations<"en"> = { FormFieldWrapper: { "reset to default": "Restablecer a los valores predeterminados" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Varios", + "Configuration that applies to all charts": + "Configuración que se aplica a todos los gráficos", + "Top level configuration values": "Valores de configuración de nivel superior" + }, YamlCodeBlockFormField: { "not an array": "Se espera un arreglo", "not an object": "Se espera un objeto", diff --git a/web/src/ui/i18n/resources/fi.tsx b/web/src/ui/i18n/resources/fi.tsx index ccf531483..1d19cdd11 100644 --- a/web/src/ui/i18n/resources/fi.tsx +++ b/web/src/ui/i18n/resources/fi.tsx @@ -667,6 +667,12 @@ Tutustu vapaasti ja ota hallintaan Kubernetes-julkaisusi! FormFieldWrapper: { "reset to default": "Palauta oletusarvoon" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Sekalaista", + "Configuration that applies to all charts": + "Konfiguraatio, joka koskee kaikkia kaavioita", + "Top level configuration values": "Ylimmän tason konfiguraatioväriarvot" + }, YamlCodeBlockFormField: { "not an array": "Taulukkoa odotetaan", "not an object": "Oliota odotetaan", diff --git a/web/src/ui/i18n/resources/fr.tsx b/web/src/ui/i18n/resources/fr.tsx index 469a7ae65..c6aa4393d 100644 --- a/web/src/ui/i18n/resources/fr.tsx +++ b/web/src/ui/i18n/resources/fr.tsx @@ -680,6 +680,12 @@ N'hésitez pas à explorer et à prendre en main vos déploiements Kubernetes ! FormFieldWrapper: { "reset to default": "Réinitialiser à la valeur par défaut" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Sekalaista", + "Configuration that applies to all charts": + "Konfiguraatio, joka koskee kaikkia kaavioita", + "Top level configuration values": "Ylimmän tason konfiguraatioväriarvot" + }, YamlCodeBlockFormField: { "not an array": "Un tableau est attendu", "not an object": "Un objet est attendu", diff --git a/web/src/ui/i18n/resources/it.tsx b/web/src/ui/i18n/resources/it.tsx index a441e61e8..d50217af2 100644 --- a/web/src/ui/i18n/resources/it.tsx +++ b/web/src/ui/i18n/resources/it.tsx @@ -674,6 +674,12 @@ Sentiti libero di esplorare e prendere il controllo dei tuoi deployment Kubernet FormFieldWrapper: { "reset to default": "Ripristina il valore predefinito" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Varie", + "Configuration that applies to all charts": + "Configurazione che si applica a tutti i grafici", + "Top level configuration values": "Valori di configurazione di livello superiore" + }, YamlCodeBlockFormField: { "not an array": "È previsto un array", "not an object": "È previsto un oggetto", diff --git a/web/src/ui/i18n/resources/nl.tsx b/web/src/ui/i18n/resources/nl.tsx index 3ab4fcedf..f1efc7507 100644 --- a/web/src/ui/i18n/resources/nl.tsx +++ b/web/src/ui/i18n/resources/nl.tsx @@ -677,6 +677,12 @@ Voel je vrij om te verkennen en de controle over je Kubernetes-implementaties te FormFieldWrapper: { "reset to default": "Terugzetten naar standaardwaarden" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Diverse", + "Configuration that applies to all charts": + "Configuratie die op alle grafieken van toepassing is", + "Top level configuration values": "Configuratiewaarden op het hoogste niveau" + }, YamlCodeBlockFormField: { "not an array": "Een array wordt verwacht", "not an object": "Een object wordt verwacht", diff --git a/web/src/ui/i18n/resources/no.tsx b/web/src/ui/i18n/resources/no.tsx index 9b358b107..ccde391fc 100644 --- a/web/src/ui/i18n/resources/no.tsx +++ b/web/src/ui/i18n/resources/no.tsx @@ -674,6 +674,12 @@ Utforsk gjerne og ta kontroll over tjenestene du kjører på Kubernetes! FormFieldWrapper: { "reset to default": "Tilbakestill til standard" }, + ConfigurationTopLevelGroup: { + miscellaneous: "Diverse", + "Configuration that applies to all charts": + "Konfigurasjon som gjelder for alle diagrammer", + "Top level configuration values": "Konfigurasjonsverdier på toppnivå" + }, YamlCodeBlockFormField: { "not an array": "En matrise forventes", "not an object": "Et objekt forventes", diff --git a/web/src/ui/i18n/resources/zh-CN.tsx b/web/src/ui/i18n/resources/zh-CN.tsx index eb6ceb547..beb5c9c04 100644 --- a/web/src/ui/i18n/resources/zh-CN.tsx +++ b/web/src/ui/i18n/resources/zh-CN.tsx @@ -628,6 +628,11 @@ ${ FormFieldWrapper: { "reset to default": "重置为默认值" }, + ConfigurationTopLevelGroup: { + miscellaneous: "杂项", + "Configuration that applies to all charts": "适用于所有图表的配置", + "Top level configuration values": "顶级配置值" + }, YamlCodeBlockFormField: { "not an array": "需要是数组", "not an object": "需要是对象", diff --git a/web/src/ui/i18n/types.ts b/web/src/ui/i18n/types.ts index c719bff0a..1a30070e1 100644 --- a/web/src/ui/i18n/types.ts +++ b/web/src/ui/i18n/types.ts @@ -56,6 +56,7 @@ export type ComponentKey = | import("ui/pages/launcher/LauncherDialogs/AutoLaunchDisabledDialog").I18n | import("ui/pages/launcher/LauncherDialogs/NoLongerBookmarkedDialog").I18n | import("ui/pages/launcher/RootFormComponent/formFields/shared/FormFieldWrapper").I18n + | import("ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup").I18n | import("ui/pages/launcher/RootFormComponent/formFields/YamlCodeBlockFormField").I18n | import("ui/pages/launcher/RootFormComponent/formFields/TextFormField").I18n | import("ui/pages/launcher/RootFormComponent/formFields/NumberFormField").I18n diff --git a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx index 36619246e..04b8b4768 100644 --- a/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx +++ b/web/src/ui/pages/launcher/RootFormComponent/ConfigurationTopLevelGroup.tsx @@ -11,6 +11,7 @@ import { createObjectThatThrowsIfAccessed, isObjectThatThrowIfAccessed } from "clean-architecture/createObjectThatThrowsIfAccessed"; +import { declareComponentKeys, useTranslation } from "ui/i18n"; type Props = { className?: string; @@ -24,6 +25,8 @@ export function ConfigurationTopLevelGroup(props: Props) { const { cx, classes } = useStyles(); + const { t } = useTranslation({ ConfigurationTopLevelGroup }); + const { accordionEntries } = useMemo(() => { const { main_formFieldGroups, main_formFields } = (() => { const main_formFields: Exclude[] = []; @@ -57,7 +60,7 @@ export function ConfigurationTopLevelGroup(props: Props) { helmValuesPath: createObjectThatThrowsIfAccessed<(string | number)[]>(), title: "global", - description: "configuration that applies to all charts", + description: t("Configuration that applies to all charts"), canAdd: false, canRemove: false, nodes: global, @@ -70,9 +73,8 @@ export function ConfigurationTopLevelGroup(props: Props) { id({ helmValuesPath: createObjectThatThrowsIfAccessed<(string | number)[]>(), - // TODO: i18n - title: "miscellaneous", - description: "Top level configuration values", + title: t("miscellaneous"), + description: t("Top level configuration values"), canAdd: false, canRemove: false, nodes: main_formFields, @@ -118,7 +120,7 @@ export function ConfigurationTopLevelGroup(props: Props) { ]; return { accordionEntries }; - }, [main]); + }, [main, t]); return (
@@ -162,3 +164,11 @@ const useStyles = tss.withName({ ConfigurationTopLevelGroup }).create(({ theme } overflow: "hidden" } })); + +const { i18n } = declareComponentKeys< + | "miscellaneous" + | "Configuration that applies to all charts" + | "Top level configuration values" +>()({ ConfigurationTopLevelGroup }); + +export type I18n = typeof i18n;