From 9f960341ee531ec0ffeda067defce5b30458a430 Mon Sep 17 00:00:00 2001 From: Ali Zamani Date: Wed, 16 Jul 2025 01:19:09 -0400 Subject: [PATCH] Fix hiding issue --- .../jsonschemaform/SolaceJsonSchemaForm.tsx | 10 +- storybook/package-lock.json | 6 +- .../layout/SolaceJsonSchemaForm.stories.tsx | 217 +++++++++++++++++- 3 files changed, 222 insertions(+), 11 deletions(-) diff --git a/src/components/jsonschemaform/SolaceJsonSchemaForm.tsx b/src/components/jsonschemaform/SolaceJsonSchemaForm.tsx index bc3e1c4b3..b723d658b 100644 --- a/src/components/jsonschemaform/SolaceJsonSchemaForm.tsx +++ b/src/components/jsonschemaform/SolaceJsonSchemaForm.tsx @@ -128,7 +128,6 @@ const hideProperties = (isHidden: any, uiSchema: UiSchema, schema: any) => { for (const property in properties) { // hide all errors - errors are displayed with custom widgets uiSchema[property] = { "ui:hideError": true }; - if (isHidden(FormFieldType.property, property, properties[property])) { // hide rjsf property widget uiSchema[property]["ui:widget"] = "hidden"; @@ -136,9 +135,14 @@ const hideProperties = (isHidden: any, uiSchema: UiSchema, schema: any) => { // hide rjsf property description via custom handling - descriptions are displayed with custom widgets uiSchema[property]["ui:description"] = CustomProperty.hidden; } + // Recursively process nested properties + if (properties[property] && typeof properties[property] === "object") { + if (!uiSchema[property]) { + uiSchema[property] = {}; + } + hideProperties(isHidden, uiSchema[property], properties[property]); + } } - - hideProperties(isHidden, uiSchema, properties); } else if (typeof schema === "object") { for (const property in schema) { hideProperties(isHidden, uiSchema, schema[property]); diff --git a/storybook/package-lock.json b/storybook/package-lock.json index d383a8df1..06e0bbcca 100644 --- a/storybook/package-lock.json +++ b/storybook/package-lock.json @@ -45,7 +45,7 @@ }, "..": { "name": "@SolaceDev/maas-react-components", - "version": "15.8.2", + "version": "15.9.0", "license": "MIT", "dependencies": { "@emotion/react": "^11.11.1", @@ -66,7 +66,6 @@ "moment-timezone": "0.5.48", "react-beautiful-dnd": "^13.1.0", "react-codemirror2": "^7.2.1", - "react-diff-view": "^3.3.1", "react-split-pane": "^0.1.92", "react-virtuoso": "^4.7.11", "react-window": "^1.8.8", @@ -76,7 +75,7 @@ "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^12.1.5", "@testing-library/user-event": "^14.5.1", - "@types/diff": "^5.0.2", + "@types/diff": "^5.2.3", "@types/jest": "^29.5.7", "@types/lodash": "^4.14.200", "@types/node": "^20.8.10", @@ -89,6 +88,7 @@ "attr-accept": "^2.2.2", "babel-eslint": "^10.1.0", "cross-env": "^7.0.3", + "diff": "^8.0.2", "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-react": "^7.33.2", diff --git a/storybook/src/stories/layout/SolaceJsonSchemaForm.stories.tsx b/storybook/src/stories/layout/SolaceJsonSchemaForm.stories.tsx index ac7a5b8f2..f4fdeec15 100644 --- a/storybook/src/stories/layout/SolaceJsonSchemaForm.stories.tsx +++ b/storybook/src/stories/layout/SolaceJsonSchemaForm.stories.tsx @@ -1,3 +1,4 @@ +import { expect } from "@storybook/jest"; /* * Copyright 2023-2025 Solace Systems. All rights reserved. * @@ -26,6 +27,7 @@ import { userEvent, within } from "@storybook/testing-library"; const demoSchema = { type: "object", title: "DemoSchema", + description: "added by me", properties: { enumProp: { default: "exclusive", @@ -111,6 +113,122 @@ const demoSchema = { required: ["enumProp", "stringProp", "stringWithPatternProp"] }; +const comprehensiveSchema = { + type: "object", + title: "Comprehensive Form Test", + description: "Tests all hiding scenarios in one schema", + properties: { + // Basic fields from demoSchema + enumProp: { + default: "exclusive", + enum: ["exclusive", "non-exclusive"], + description: "enumProp description", + type: "string" + }, + booleanProp: { + default: true, + description: "booleanProp description", + type: "boolean" + }, + stringProp: { + default: "#DEAD_MSG_QUEUE", + minLength: 10, + maxLength: 30, + description: "stringProp description", + type: "string" + }, + constProp: { + const: "no-access", + description: "constProp description (should be hidden)", + type: "string" + }, + longStringProp: { + type: "string", + description: "longStringProp description (should be hidden)", + maxLength: 500 + }, + + // Nested object testing + nestedObject: { + type: "object", + title: "Nested Object", + properties: { + visibleNested: { + type: "string", + title: "Visible Nested", + description: "This nested field should be visible" + }, + hiddenNested: { + type: "string", + title: "Hidden Nested", + description: "This nested field should be hidden" + }, + deeplyNested: { + type: "object", + title: "Deeply Nested", + properties: { + visibleDeep: { + type: "string", + title: "Visible Deep", + description: "This deeply nested field should be visible" + }, + hiddenDeep: { + type: "string", + title: "Hidden Deep", + description: "This deeply nested field should be hidden" + } + } + } + } + }, + + // AnyOf conditional testing + conditionalSection: { + type: "object", + title: "Conditional Section", + properties: { + conditionalField: { + anyOf: [ + { + title: "Option A", + type: "object", + properties: { + optionA: { + type: "string", + title: "Option A Field" + }, + hiddenInA: { + type: "string", + title: "Hidden in A" + } + } + }, + { + title: "Option B", + type: "object", + properties: { + optionB: { + type: "string", + title: "Option B Field" + }, + hiddenInB: { + type: "string", + title: "Hidden in B" + } + } + } + ] + }, + regularNested: { + type: "string", + title: "Regular Nested Field" + } + } + } + }, + required: ["enumProp", "stringProp"] +}; + const defaultTransformError = (error) => { const { name, message } = error; let newMessage; @@ -334,8 +452,8 @@ export const Validation: Story = { export const Hidden: Story = { args: { formItem: { - id: "demoSchema1", - schema: demoSchema + id: "comprehensiveHiding", + schema: comprehensiveSchema }, formOptions: { isHidden: (fieldType, propertyName, data) => { @@ -345,7 +463,15 @@ export const Hidden: Story = { case "description": return true; case "property": - return data?.const !== undefined || propertyName === "stringLong"; + // Hide fields based on property name patterns + return ( + data?.const !== undefined || + propertyName === "longStringProp" || + propertyName === "hiddenNested" || + propertyName === "hiddenDeep" || + propertyName === "hiddenInA" || + propertyName === "hiddenInB" + ); default: return false; } @@ -353,14 +479,95 @@ export const Hidden: Story = { tagName: "div" }, formData: { - arrayProp: [{ name: "Bob", accessLevel: 50 }], - longPasswordStringProp: "x".repeat(500) + enumProp: "exclusive", + stringProp: "test value", + booleanProp: true, + constProp: "no-access", + longStringProp: "should be hidden", + nestedObject: { + visibleNested: "visible nested value", + hiddenNested: "this should be hidden", + deeplyNested: { + visibleDeep: "visible deep value", + hiddenDeep: "this should also be hidden" + } + }, + conditionalSection: { + conditionalField: { + optionA: "option A value", + hiddenInA: "this should be hidden" + }, + regularNested: "regular nested value" + } }, liveValidate: false, onChange: (data, errors) => action("onChangeHandler")(data, errors), transformError: defaultTransformError, transformWidget: defaultTransform, transformTitle: defaultTransform + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + await step("Test basic field hiding", async () => { + // Assert visible basic fields are present + expect(canvas.getByText("enumProp")).toBeInTheDocument(); + expect(canvas.getByText("stringProp")).toBeInTheDocument(); + expect(canvas.getByText("booleanProp")).toBeInTheDocument(); + + // Assert hidden basic fields are not present + expect(canvas.queryByText("longStringProp")).not.toBeInTheDocument(); + expect(canvas.queryByText("constProp")).not.toBeInTheDocument(); + }); + + await step("Test nested field hiding", async () => { + // Assert visible nested fields are present + expect(canvas.getByText("Visible Nested")).toBeInTheDocument(); + expect(canvas.getByText("Visible Deep")).toBeInTheDocument(); + + // Assert hidden nested fields are not present + expect(canvas.queryByText("Hidden Nested")).not.toBeInTheDocument(); + expect(canvas.queryByText("Hidden Deep")).not.toBeInTheDocument(); + }); + + await step("Test anyOf conditional hiding - Option A", async () => { + // Assert Option A is initially selected and visible + expect(canvas.getByText("Option A Field")).toBeInTheDocument(); + expect(canvas.getByDisplayValue("option A value")).toBeInTheDocument(); + expect(canvas.getByText("Regular Nested Field")).toBeInTheDocument(); + + // Assert hiddenInA field is not present + expect(canvas.queryByText("Hidden in A")).not.toBeInTheDocument(); + }); + + await step("Test anyOf conditional hiding - Option B", async () => { + // Switch to Option B + const dropdown = canvas.getAllByText("Option A"); + await userEvent.click(dropdown[0]); + + await new Promise((resolve) => setTimeout(resolve, 500)); + await userEvent.keyboard("[ArrowDown]"); + await userEvent.keyboard("[Enter]"); + + // Wait for form to update + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Assert Option B field is now visible + expect(canvas.getByLabelText("Option B Field")).toBeInTheDocument(); + + // Assert hiddenInB field is not present + expect(canvas.queryByLabelText("Hidden in B")).not.toBeInTheDocument(); + + // Assert Option A field is no longer visible + expect(canvas.queryByLabelText("Option A Field")).not.toBeInTheDocument(); + }); + + await step("Test form structure integrity", async () => { + // Assert submit button and titles are hidden + expect(canvas.queryByRole("button", { name: "Submit" })).not.toBeInTheDocument(); + expect(canvas.queryByText("Comprehensive Form Test")).not.toBeInTheDocument(); + expect(canvas.queryByText("Tests all hiding scenarios in one schema")).not.toBeInTheDocument(); + }); } };