diff --git a/src/renderer/src/stories/JSONSchemaForm.js b/src/renderer/src/stories/JSONSchemaForm.js index 73199dd63..4bbc76abf 100644 --- a/src/renderer/src/stories/JSONSchemaForm.js +++ b/src/renderer/src/stories/JSONSchemaForm.js @@ -113,6 +113,11 @@ hr { color: #ff0033; } + :host([requirementmode="loose"]) .required label:after { + color: gray; + } + + .required.conditional label:after { color: transparent; } @@ -152,6 +157,7 @@ export class JSONSchemaForm extends LitElement { required: { type: Object, reflect: false }, dialogType: { type: String, reflect: false }, dialogOptions: { type: Object, reflect: false }, + requirementMode: { type: String, reflect: true }, }; } @@ -189,6 +195,7 @@ export class JSONSchemaForm extends LitElement { this.dialogType = props.dialogType; this.deferLoading = props.deferLoading ?? false; + this.requirementMode = props.requirementMode ?? "default"; this.onlyRequired = props.onlyRequired ?? false; this.showLevelOverride = props.showLevelOverride ?? false; @@ -302,7 +309,7 @@ export class JSONSchemaForm extends LitElement { validate = async (resolved) => { // Check if any required inputs are missing const invalidInputs = await this.#validateRequirements(resolved); // get missing required paths - const isValid = !invalidInputs.length; + const isValid = this.requirementMode === "loose" ? true : !invalidInputs.length; // Print out a detailed error message if any inputs are missing let message = isValid ? "" : `${invalidInputs.length} required inputs are not specified properly.`; diff --git a/src/renderer/src/stories/pages/FormPage.js b/src/renderer/src/stories/pages/FormPage.js index 1cd9ddc28..de82cceab 100644 --- a/src/renderer/src/stories/pages/FormPage.js +++ b/src/renderer/src/stories/pages/FormPage.js @@ -10,12 +10,12 @@ export function schemaToPages(schema, globalStatePath, options, transformationCa .filter(([_, value]) => value.properties) .map(([key, value]) => { const optionsCopy = { ...options }; + if (optionsCopy.required && optionsCopy.required[key]) optionsCopy.required = { [key]: optionsCopy.required[key], }; - // Only bring requirements from the current page - else delete optionsCopy.required; + else delete optionsCopy.required; // Only bring requirements from the current page const page = new GuidedFormPage( transformationCallback({ diff --git a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js index 75c8054ff..70bb960ab 100644 --- a/src/renderer/src/stories/pages/guided-mode/GuidedStart.js +++ b/src/renderer/src/stories/pages/guided-mode/GuidedStart.js @@ -65,16 +65,27 @@ export class GuidedStartPage extends Page {
The second section will have you provide your source data files and NWB File metadata on a per-subject basis to populate your files.
+ ${new InspectorListItem({ - message: html`Red boxes are error messages. These will block your conversion progress + message: html`Red boxes are Error messages. These will block your conversion progress until resolved.`, type: "error", })} ${new InspectorListItem({ - message: html`Yellow boxes are warnings. Fixing them will align your NWB files with - best practices.`, + message: html`Yellow boxes are Warning messages. Fixing them will align your NWB + files with best practices.`, type: "warning", })} + ++ Throughout the forms found in the GUIDE, asterisks (*) represent required properties. + Attempting to move forward will throw an Error until these properties are filled in. +
++ Gray asterisks (*) are sometimes used to represent loose requirements, where missing this property will throw an Error + — though you don't need to specify a value at the current stage. +
+In the third section, you will preview your conversion before uploading to DANDI. diff --git a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js index 662adc7ff..7e44b9b5a 100644 --- a/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js +++ b/src/renderer/src/stories/pages/guided-mode/setup/GuidedNewDatasetInfo.js @@ -30,14 +30,11 @@ const changesAcrossSessions = { const projectMetadataSchema = merge(projectGlobalSchema, projectGeneralSchema); Object.entries(baseMetadataSchema.properties).forEach(([globalProp, v]) => { - Object.keys(v.properties) - .filter((prop) => !(changesAcrossSessions[globalProp] ?? []).includes(prop)) - .forEach((prop) => { - const globalNestedProp = - projectMetadataSchema.properties[globalProp] ?? - (projectMetadataSchema.properties[globalProp] = { properties: {} }); - globalNestedProp.properties[prop] = baseMetadataSchema.properties[globalProp].properties[prop]; - }); + const info = (projectMetadataSchema.properties[globalProp] = structuredClone(v)); + + changesAcrossSessions[globalProp]?.forEach((prop) => { + delete info.properties[prop]; + }); }); export class GuidedNewDatasetPage extends Page { @@ -111,10 +108,16 @@ export class GuidedNewDatasetPage extends Page { this.state = merge(global.data.output_locations, structuredClone(this.info.globalState.project)); - const pages = schemaToPages.call(this, schema, ["project"], { validateEmptyValues: false }, (info) => { - info.title = `${info.label} Global Metadata`; - return info; - }); + const pages = schemaToPages.call( + this, + schema, + ["project"], + { validateEmptyValues: false, requirementMode: "loose" }, + (info) => { + info.title = `${info.label} Global Metadata`; + return info; + } + ); pages.forEach((page) => { page.header = {