diff --git a/dist/js/context/providers/PointsGrid/IGridFormDataManager.js b/dist/js/context/providers/PointsGrid/IGridFormDataManager.js index de182bc7..61c94be9 100644 --- a/dist/js/context/providers/PointsGrid/IGridFormDataManager.js +++ b/dist/js/context/providers/PointsGrid/IGridFormDataManager.js @@ -3,17 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const JSONSchemasInterface_1 = __importDefault(require("@mat3ra/esse/dist/js/esse/JSONSchemasInterface")); const PointsGridFormDataProvider_1 = __importDefault(require("./PointsGridFormDataProvider")); class IGridFormDataManager extends PointsGridFormDataProvider_1.default { constructor(contextItem, externalContext) { super(contextItem, externalContext, 0.2); this.name = "igrid"; - const jsonSchema = JSONSchemasInterface_1.default.getPatchedSchemaById(this.jsonSchemaId, this.jsonSchemaPatchConfig); - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext, externalContext) { const contextItem = this.findContextItem(unitContext, "igrid"); diff --git a/dist/js/context/providers/PointsGrid/KGridFormDataManager.js b/dist/js/context/providers/PointsGrid/KGridFormDataManager.js index 9a862fd6..688e4a4b 100644 --- a/dist/js/context/providers/PointsGrid/KGridFormDataManager.js +++ b/dist/js/context/providers/PointsGrid/KGridFormDataManager.js @@ -3,17 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const JSONSchemasInterface_1 = __importDefault(require("@mat3ra/esse/dist/js/esse/JSONSchemasInterface")); const PointsGridFormDataProvider_1 = __importDefault(require("./PointsGridFormDataProvider")); class KGridFormDataManager extends PointsGridFormDataProvider_1.default { constructor(contextItem, externalContext) { super(contextItem, externalContext, 1); this.name = "kgrid"; - const jsonSchema = JSONSchemasInterface_1.default.getPatchedSchemaById(this.jsonSchemaId, this.jsonSchemaPatchConfig); - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext, externalContext) { const contextItem = this.findContextItem(unitContext, "kgrid"); diff --git a/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.d.ts b/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.d.ts index bb1d3908..fa92b328 100644 --- a/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.d.ts +++ b/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.d.ts @@ -36,16 +36,6 @@ declare abstract class PointsGridFormDataProvider exte getData(): Data; getDefaultData(): PointsGridDataProviderSchema; protected get jsonSchemaPatchConfig(): { - dimensions: { - default?: any[] | undefined; - type: string; - items: { - default?: string | number | readonly number[] | readonly string[] | undefined; - type: string; - }; - minItems: number; - maxItems: number; - }; shifts: { default?: any[] | undefined; type: string; @@ -113,6 +103,11 @@ declare abstract class PointsGridFormDataProvider exte }; }; }; + /** + * Form schema for RJSF. Replaces ESSE `dimensions.anyOf` (number[] | string[]) with a single + * array type — patch merge cannot remove `anyOf`, which makes RJSF render a branch picker. + */ + protected buildFormJsonSchema(): JSONSchema7; /** Prefer persisted `data` — `setData` runs before React re-inits the provider on render. */ private get preferGridMetricForUi(); get uiSchema(): { diff --git a/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.js b/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.js index c9e345e9..5c1e2e9a 100644 --- a/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.js +++ b/dist/js/context/providers/PointsGrid/PointsGridFormDataProvider.js @@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("@mat3ra/code/dist/js/constants"); const math_1 = require("@mat3ra/code/dist/js/math"); +const JSONSchemasInterface_1 = __importDefault(require("@mat3ra/esse/dist/js/esse/JSONSchemasInterface")); const made_1 = require("@mat3ra/made"); const MaterialContextMixin_1 = __importDefault(require("../../mixins/MaterialContextMixin")); const JSONSchemaFormDataProvider_1 = __importDefault(require("../base/JSONSchemaFormDataProvider")); @@ -112,7 +113,6 @@ class PointsGridFormDataProvider extends JSONSchemaFormDataProvider_1.default { }; const gridMetricType = ((_a = this.data) === null || _a === void 0 ? void 0 : _a.gridMetricType) || this.defaultMetric.type; return { - dimensions: vector(this.defaultDimensions, this.isUsingJinjaVariables), shifts: vector(defaultShifts), reciprocalVectorRatios: vector(this.reciprocalVectorRatios), gridMetricType: { default: this.defaultMetric.type }, @@ -158,6 +158,18 @@ class PointsGridFormDataProvider extends JSONSchemaFormDataProvider_1.default { }, }; } + /** + * Form schema for RJSF. Replaces ESSE `dimensions.anyOf` (number[] | string[]) with a single + * array type — patch merge cannot remove `anyOf`, which makes RJSF render a branch picker. + */ + buildFormJsonSchema() { + const jsonSchema = JSONSchemasInterface_1.default.getPatchedSchemaById(this.jsonSchemaId, this.jsonSchemaPatchConfig); + if (!(jsonSchema === null || jsonSchema === void 0 ? void 0 : jsonSchema.properties)) { + throw new Error("Failed to get patched JSON schema"); + } + jsonSchema.properties.dimensions = vector(this.defaultDimensions, this.isUsingJinjaVariables); + return jsonSchema; + } /** Prefer persisted `data` — `setData` runs before React re-inits the provider on render. */ get preferGridMetricForUi() { var _a, _b; diff --git a/dist/js/context/providers/PointsGrid/QGridFormDataManager.js b/dist/js/context/providers/PointsGrid/QGridFormDataManager.js index 0b3ee873..d440a010 100644 --- a/dist/js/context/providers/PointsGrid/QGridFormDataManager.js +++ b/dist/js/context/providers/PointsGrid/QGridFormDataManager.js @@ -3,17 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const JSONSchemasInterface_1 = __importDefault(require("@mat3ra/esse/dist/js/esse/JSONSchemasInterface")); const PointsGridFormDataProvider_1 = __importDefault(require("./PointsGridFormDataProvider")); class QGridFormDataManager extends PointsGridFormDataProvider_1.default { constructor(contextItem, externalContext) { super(contextItem, externalContext, 5); this.name = "qgrid"; - const jsonSchema = JSONSchemasInterface_1.default.getPatchedSchemaById(this.jsonSchemaId, this.jsonSchemaPatchConfig); - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext, externalContext) { const contextItem = this.findContextItem(unitContext, "qgrid"); diff --git a/src/js/context/providers/PointsGrid/IGridFormDataManager.ts b/src/js/context/providers/PointsGrid/IGridFormDataManager.ts index b9263d7c..b6ecb3c6 100644 --- a/src/js/context/providers/PointsGrid/IGridFormDataManager.ts +++ b/src/js/context/providers/PointsGrid/IGridFormDataManager.ts @@ -1,4 +1,3 @@ -import JSONSchemasInterface from "@mat3ra/esse/dist/js/esse/JSONSchemasInterface"; import type { GridContextItemSchema } from "@mat3ra/esse/dist/js/types"; import type { JSONSchema7 } from "json-schema"; @@ -16,16 +15,7 @@ export default class IGridFormDataManager extends PointsGridFormDataProvider, externalContext: ExternalContext) { super(contextItem, externalContext, 0.2); - const jsonSchema = JSONSchemasInterface.getPatchedSchemaById( - this.jsonSchemaId, - this.jsonSchemaPatchConfig, - ); - - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext: UnitContext, externalContext: ExternalContext) { diff --git a/src/js/context/providers/PointsGrid/KGridFormDataManager.ts b/src/js/context/providers/PointsGrid/KGridFormDataManager.ts index 4c7e9c71..8973314e 100644 --- a/src/js/context/providers/PointsGrid/KGridFormDataManager.ts +++ b/src/js/context/providers/PointsGrid/KGridFormDataManager.ts @@ -1,4 +1,3 @@ -import JSONSchemasInterface from "@mat3ra/esse/dist/js/esse/JSONSchemasInterface"; import type { GridContextItemSchema } from "@mat3ra/esse/dist/js/types"; import type { JSONSchema7 } from "json-schema"; @@ -17,16 +16,7 @@ export default class KGridFormDataManager extends PointsGridFormDataProvider, externalContext: ExternalContext) { super(contextItem, externalContext, 1); - const jsonSchema = JSONSchemasInterface.getPatchedSchemaById( - this.jsonSchemaId, - this.jsonSchemaPatchConfig, - ); - - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext: UnitContext, externalContext: ExternalContext) { diff --git a/src/js/context/providers/PointsGrid/PointsGridFormDataProvider.ts b/src/js/context/providers/PointsGrid/PointsGridFormDataProvider.ts index 386cd37b..b870280f 100644 --- a/src/js/context/providers/PointsGrid/PointsGridFormDataProvider.ts +++ b/src/js/context/providers/PointsGrid/PointsGridFormDataProvider.ts @@ -1,13 +1,14 @@ import { Units } from "@mat3ra/code/dist/js/constants"; import { math as codeJSMath } from "@mat3ra/code/dist/js/math"; import type { Constructor } from "@mat3ra/code/dist/js/utils/types"; +import JSONSchemasInterface from "@mat3ra/esse/dist/js/esse/JSONSchemasInterface"; import type { GridContextItemSchema, PointsGridDataProviderSchema, Vector3DSchema, } from "@mat3ra/esse/dist/js/types"; import { type ReciprocalLattice, Made } from "@mat3ra/made"; -import type { JSONSchema7 } from "json-schema"; +import type { JSONSchema7, JSONSchema7Definition } from "json-schema"; import materialContextMixin, { type MaterialContextMixin, @@ -81,6 +82,8 @@ abstract class PointsGridFormDataProvider< value: number; }; + // Assigned in subclass constructors via buildFormJsonSchema() — not in this constructor: + // jsonSchemaPatchConfig uses this.name, which is only set after super() returns. abstract readonly jsonSchema: JSONSchema7; constructor(contextItem: Partial, externalContext: ExternalContext, divisor: number) { @@ -186,7 +189,6 @@ abstract class PointsGridFormDataProvider< const gridMetricType = this.data?.gridMetricType || this.defaultMetric.type; return { - dimensions: vector(this.defaultDimensions, this.isUsingJinjaVariables), shifts: vector(defaultShifts), reciprocalVectorRatios: vector(this.reciprocalVectorRatios), gridMetricType: { default: this.defaultMetric.type }, @@ -235,6 +237,28 @@ abstract class PointsGridFormDataProvider< }; } + /** + * Form schema for RJSF. Replaces ESSE `dimensions.anyOf` (number[] | string[]) with a single + * array type — patch merge cannot remove `anyOf`, which makes RJSF render a branch picker. + */ + protected buildFormJsonSchema(): JSONSchema7 { + const jsonSchema = JSONSchemasInterface.getPatchedSchemaById( + this.jsonSchemaId, + this.jsonSchemaPatchConfig, + ); + + if (!jsonSchema?.properties) { + throw new Error("Failed to get patched JSON schema"); + } + + jsonSchema.properties.dimensions = vector( + this.defaultDimensions, + this.isUsingJinjaVariables, + ) as JSONSchema7Definition; + + return jsonSchema; + } + /** Prefer persisted `data` — `setData` runs before React re-inits the provider on render. */ private get preferGridMetricForUi() { return this.data?.preferGridMetric ?? this.preferGridMetric; diff --git a/src/js/context/providers/PointsGrid/QGridFormDataManager.ts b/src/js/context/providers/PointsGrid/QGridFormDataManager.ts index 39ed09a1..05c068c8 100644 --- a/src/js/context/providers/PointsGrid/QGridFormDataManager.ts +++ b/src/js/context/providers/PointsGrid/QGridFormDataManager.ts @@ -1,4 +1,3 @@ -import JSONSchemasInterface from "@mat3ra/esse/dist/js/esse/JSONSchemasInterface"; import type { GridContextItemSchema } from "@mat3ra/esse/dist/js/types"; import type { JSONSchema7 } from "json-schema"; @@ -16,16 +15,7 @@ export default class QGridFormDataManager extends PointsGridFormDataProvider, externalContext: ExternalContext) { super(contextItem, externalContext, 5); - const jsonSchema = JSONSchemasInterface.getPatchedSchemaById( - this.jsonSchemaId, - this.jsonSchemaPatchConfig, - ); - - if (!jsonSchema) { - throw new Error("Failed to get patched JSON schema"); - } - - this.jsonSchema = jsonSchema; + this.jsonSchema = this.buildFormJsonSchema(); } static createFromUnitContext(unitContext: UnitContext, externalContext: ExternalContext) { diff --git a/tests/js/pointsGridFormDataProvider.test.ts b/tests/js/pointsGridFormDataProvider.test.ts new file mode 100644 index 00000000..34404f5a --- /dev/null +++ b/tests/js/pointsGridFormDataProvider.test.ts @@ -0,0 +1,67 @@ +import { + type InMemoryEntityInSet, + inMemoryEntityInSetMixin, +} from "@mat3ra/code/dist/js/entity/set/InMemoryEntityInSetMixin"; +import { + type OrderedInMemoryEntityInSet, + orderedEntityInSetMixin, +} from "@mat3ra/code/dist/js/entity/set/ordered/OrderedInMemoryEntityInSetMixin"; +import JSONSchemasInterface from "@mat3ra/esse/dist/js/esse/JSONSchemasInterface"; +import esseSchemas from "@mat3ra/esse/dist/js/schemas.json"; +import { Material } from "@mat3ra/made"; +import { expect } from "chai"; +import type { JSONSchema7, JSONSchema7Definition } from "json-schema"; + +import KGridFormDataManager from "../../src/js/context/providers/PointsGrid/KGridFormDataManager"; + +interface OrderedMaterial extends OrderedInMemoryEntityInSet, InMemoryEntityInSet {} + +class OrderedMaterial extends Material implements OrderedInMemoryEntityInSet { + declare static createDefault: () => OrderedMaterial; +} + +inMemoryEntityInSetMixin(OrderedMaterial.prototype); +orderedEntityInSetMixin(OrderedMaterial.prototype); + +function getDimensionsSchema(jsonSchema: JSONSchema7): JSONSchema7Definition { + const dimensions = jsonSchema.properties?.dimensions; + expect(dimensions).to.be.an("object"); + return dimensions as JSONSchema7Definition; +} + +describe("PointsGridFormDataProvider form jsonSchema", () => { + before(() => { + JSONSchemasInterface.setSchemas(esseSchemas as JSONSchema7[]); + }); + + it("exposes number[] dimensions without anyOf for RJSF", () => { + const material = OrderedMaterial.createDefault(); + material.hash = material.calculateHash(); + + const provider = new KGridFormDataManager( + { name: "kgrid" }, + { material, isUsingJinjaVariables: false }, + ); + + const dimensionsSchema = getDimensionsSchema(provider.jsonSchema); + + expect(dimensionsSchema).to.not.have.property("anyOf"); + expect(dimensionsSchema).to.deep.include({ type: "array", minItems: 3, maxItems: 3 }); + expect((dimensionsSchema as JSONSchema7).items).to.deep.include({ type: "number" }); + }); + + it("exposes string[] dimensions when using Jinja variables", () => { + const material = OrderedMaterial.createDefault(); + material.hash = material.calculateHash(); + + const provider = new KGridFormDataManager( + { name: "kgrid" }, + { material, isUsingJinjaVariables: true }, + ); + + const dimensionsSchema = getDimensionsSchema(provider.jsonSchema); + + expect(dimensionsSchema).to.not.have.property("anyOf"); + expect((dimensionsSchema as JSONSchema7).items).to.deep.include({ type: "string" }); + }); +}); diff --git a/tests/js/subworkflow.standata.integration.test.ts b/tests/js/subworkflow.standata.integration.test.ts index 3cd8a540..dd675aba 100644 --- a/tests/js/subworkflow.standata.integration.test.ts +++ b/tests/js/subworkflow.standata.integration.test.ts @@ -39,7 +39,10 @@ describe("Subworkflow", () => { ApplicationRegistry.setDriver(new StandataDriver()); }); - it("addConvergence on first subworkflow then workflow.render for every standata workflow (when applicable)", () => { + it("addConvergence on first subworkflow then workflow.render for every standata workflow (when applicable)", function () { + // Renders every standata workflow; ~1s locally but exceeds Mocha's default 2s on GitHub Actions. + this.timeout(10000); + const standataWorkflows = new WorkflowStandata().getAll() as unknown as WorkflowSchema[]; expect(standataWorkflows.length).to.be.above(0);