Skip to content

Commit

Permalink
First Round of Updates from Oliver User Test (#548)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Cody Baker <[email protected]>
  • Loading branch information
3 people authored Jan 3, 2024
1 parent 249089d commit 8da7ebe
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 76 deletions.
14 changes: 12 additions & 2 deletions schemas/base-metadata.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,27 @@ export const preprocessMetadataSchema = (schema: any = baseMetadataSchema, globa
description: 'The species of your subject.'
}

// copy.order = ['NWBFile', 'Subject']

copy.properties.NWBFile.title = 'General Metadata'
const nwbProps = copy.properties.NWBFile.properties
nwbProps.keywords.items.description = "Provide a single keyword (e.g. Neural circuits, V1, etc.)"

// Resolve species suggestions
resolve(serverGlobals.species, (res) => {
const info = getSpeciesInfo(res)
Object.assign(subjectProps.species, info)
})

// Ensure experimenter schema has custom structure
copy.properties.NWBFile.properties.experimenter = baseMetadataSchema.properties.NWBFile.properties.experimenter
nwbProps.experimenter = baseMetadataSchema.properties.NWBFile.properties.experimenter

// Ensure related_publications schema has custom structure
nwbProps.related_publications = baseMetadataSchema.properties.NWBFile.properties.related_publications


// Override description of keywords
copy.properties.NWBFile.properties.keywords.description = 'Terms to describe your dataset (e.g. Neural circuits, V1, etc.)' // Add description to keywords
nwbProps.keywords.description = 'Terms to describe your dataset (e.g. Neural circuits, V1, etc.)' // Add description to keywords



Expand Down
14 changes: 12 additions & 2 deletions schemas/json/base_metadata_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,19 @@
},
"related_publications": {
"type": "array",
"description": "Provide a DOI for each publication.",
"items": {
"title": "related publication",
"type": "string"
"type": "string",
"format": "{doi}",
"properties": {
"doi": {
"title": "DOI",
"pattern": "^(doi:|http:\/\/dx\\.doi\\.org/|https:\/\/doi\\.org\/)\\b(10[.][0-9]{4,}(?:[.][0-9]+)*\\/(?:(?![\"&\\'<>])\\S)+)\\b$",
"type": "string",
"description": "Provide your publication formatted as a digital object identifier (DOI), e.g., doi:10.1038/nn.1234 or https://doi.org/10.1038/nn.1234."
}
},
"required": [ "doi" ]
}
},
"slices": {
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/src/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const pages = {
}),

structure: new GuidedStructurePage({
title: "Data Formats",
title: "Provide Data Formats",
label: "Data formats",
section: sections[0],
}),
Expand All @@ -138,7 +138,7 @@ const pages = {
}),

sourcedata: new GuidedSourceDataPage({
title: "Source Data",
title: "Source Data Information",
label: "Source data",
section: sections[1],
}),
Expand Down
110 changes: 76 additions & 34 deletions src/renderer/src/stories/JSONSchemaForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { JSONSchemaInput } from "./JSONSchemaInput";
import { InspectorListItem } from "./preview/inspector/InspectorList";

import { Validator } from "jsonschema";
import { successHue, warningHue, errorHue } from "./globals";

var v = new Validator();

Expand Down Expand Up @@ -51,7 +52,7 @@ const componentCSS = `
.guided--form-label {
display: block;
width: 100%;
margin: 1.45rem 0 0.45rem 0;
margin: 1.45rem 0 0 0;
color: black;
font-weight: 600;
}
Expand All @@ -60,6 +61,10 @@ const componentCSS = `
margin-top: 0;
}
jsonschema-input {
margin-top: 0.5rem;
}
.guided--form-label {
font-size: 1.2em !important;
}
Expand Down Expand Up @@ -94,18 +99,26 @@ const componentCSS = `
font-weight: bold;
}
.link.required::after {
box-sizing: border-box;
display: block;
width: 10px;
height: 10px;
background: #ff3d64;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
content: '';
margin: 15px;
.link::after {
box-sizing: border-box;
display: block;
width: 10px;
height: 10px;
background: hsl(${successHue}, 100%, 70%) !important;
border-radius: 50%;
position: absolute;
top: 0;
right: 0;
content: '';
margin: 15px;
}
.link.error::after {
background: hsl(${errorHue}, 100%, 70%) !important;
}
.link.warning::after {
background: hsl(${warningHue}, 100%, 70%) !important;
}
hr {
Expand All @@ -123,7 +136,7 @@ const componentCSS = `
}
.required label:after {
content: " *";
content: "*";
color: #ff0033;
}
Expand Down Expand Up @@ -162,6 +175,10 @@ const componentCSS = `
opacity: 0.5;
pointer-events: none;
}
small {
font-size: 0.8em;
}
`;

document.addEventListener("dragover", (e) => {
Expand All @@ -188,6 +205,8 @@ export class JSONSchemaForm extends LitElement {

base = [];
#nestedForms = {};
inputs = [];

tables = {};
#nErrors = 0;
#nWarnings = 0;
Expand Down Expand Up @@ -219,6 +238,8 @@ export class JSONSchemaForm extends LitElement {
this.dialogType = props.dialogType;
this.deferLoading = props.deferLoading ?? false;

this.showPath = props.showPath ?? false;

this.controls = props.controls ?? {};

this.transformErrors = props.transformErrors;
Expand All @@ -228,7 +249,7 @@ export class JSONSchemaForm extends LitElement {
this.onlyRequired = props.onlyRequired ?? false;
this.showLevelOverride = props.showLevelOverride ?? false;

this.conditionalRequirements = props.conditionalRequirements ?? []; // NOTE: We assume properties only belong to one conditional requirement group
this.groups = props.groups ?? []; // NOTE: We assume properties only belong to one conditional requirement group

this.validateEmptyValues = props.validateEmptyValues ?? true;

Expand Down Expand Up @@ -514,6 +535,8 @@ export class JSONSchemaForm extends LitElement {
validateEmptyValue: this.validateEmptyValues,
});

this.inputs.push(interactiveInput);

// this.validateEmptyValues ? undefined : (el) => (el.value ?? el.checked) !== ""

// const possibleInputs = Array.from(this.shadowRoot.querySelectorAll("jsonschema-input")).map(input => input.children)
Expand All @@ -532,7 +555,15 @@ export class JSONSchemaForm extends LitElement {
? "conditional"
: ""}"
>
<label class="guided--form-label">${info.title ?? header(name)}</label>
<label class="guided--form-label">${info.title ?? header(name)} </label>
${this.showPath
? html` <small
>${externalPath
.slice(0, -1)
.map((str) => header(str ?? ""))
.join(".")}</small
>`
: ""}
${interactiveInput}
<div class="errors"></div>
<div class="warnings"></div>
Expand Down Expand Up @@ -645,9 +676,15 @@ export class JSONSchemaForm extends LitElement {

#getLink = (args) => {
if (typeof args === "string") args = args.split("-");
return this.conditionalRequirements.find((linked) =>
linked.properties.find((link) => link.join("-") === args.join("-"))
);
const group = this.#getGroup(args);
if (!group) return;
return group.validate ? group : undefined;
};

#getGroup = (args) => {
if (typeof args === "string") args = args.split("-");
const group = this.groups.find((linked) => linked.properties.find((link) => link.join("-") === args.join("-")));
return group;
};

#applyToLinkedProperties = (fn, externalPath) => {
Expand Down Expand Up @@ -683,8 +720,8 @@ export class JSONSchemaForm extends LitElement {
return res;
};

#getLinkElement = (externalPath) => {
const link = this.#getLink(externalPath);
#getGroupElement = (externalPath) => {
const link = this.#getGroup(externalPath);
if (!link) return;
return this.shadowRoot.querySelector(`[data-name="${link.name}"]`);
};
Expand Down Expand Up @@ -823,12 +860,16 @@ export class JSONSchemaForm extends LitElement {

const input = this.getInput(localPath);

const groupEl = this.#getGroupElement(externalPath);

if (groupEl) {
groupEl.classList[resolvedErrors.length ? "add" : "remove"]("error");
groupEl.classList[warnings.length ? "add" : "remove"]("warning");
}

if (isValid && resolvedErrors.length === 0) {
input.classList.remove("invalid");

const linkEl = this.#getLinkElement(externalPath);
if (linkEl) linkEl.classList.remove("required", "conditional");

await this.#applyToLinkedProperties((path, element) => {
element.classList.remove("required", "conditional"); // Links manage their own error and validity states, but only one needs to be valid
}, localPath);
Expand All @@ -840,9 +881,6 @@ export class JSONSchemaForm extends LitElement {
// Add new invalid classes and errors
input.classList.add("invalid");

const linkEl = this.#getLinkElement(externalPath);
if (linkEl) linkEl.classList.add("required", "conditional");

// Only add the conditional class for linked elements
await this.#applyToLinkedProperties(
(name, element) => element.classList.add("required", "conditional"),
Expand Down Expand Up @@ -870,8 +908,7 @@ export class JSONSchemaForm extends LitElement {

let renderableWithLinks = renderable.reduce((acc, [name, info]) => {
const externalPath = [...this.base, ...path, name];
const link = this.#getLink(externalPath); // Use the base path to find a link

const link = this.#getGroup(externalPath); // Use the base path to find a link
if (link) {
if (!acc.find(([_, info]) => info === link)) {
const entry = [link.name, link];
Expand All @@ -887,10 +924,12 @@ export class JSONSchemaForm extends LitElement {

// Sort alphabetically
.sort(([name], [name2]) => {
if (name.toLowerCase() < name2.toLowerCase()) {
const header1 = header(name);
const header2 = header(name2);
if (header1.toLowerCase() < header2.toLowerCase()) {
return -1;
}
if (name.toLowerCase() > name2.toLowerCase()) {
if (header1.toLowerCase() > header2.toLowerCase()) {
return 1;
}
return 0;
Expand All @@ -917,7 +956,8 @@ export class JSONSchemaForm extends LitElement {

if (e1[isLink] || e2[isLink]) return 0;

if (info2.properties) return -1;
if (info2.properties && info.properties) return 0;
else if (info2.properties) return -1;
else if (info.properties) return 1;
else return 0;
});
Expand Down Expand Up @@ -976,7 +1016,7 @@ export class JSONSchemaForm extends LitElement {
style: "margin-right: 10px; pointer-events:all;",
});

const headerName = header(name);
const headerName = header(info.title ?? name);

const renderableInside = this.#getRenderable(info, required[name], localPath, true);

Expand Down Expand Up @@ -1015,12 +1055,13 @@ export class JSONSchemaForm extends LitElement {

required: required[name], // Scoped to the sub-schema
ignore: this.ignore,
showPath: this.showPath,
dialogOptions: this.dialogOptions,
dialogType: this.dialogType,
onlyRequired: this.onlyRequired,
showLevelOverride: this.showLevelOverride,
deferLoading: this.deferLoading,
conditionalRequirements: this.conditionalRequirements,
groups: this.groups,
validateOnChange: (...args) => this.validateOnChange(...args),
onThrow: (...args) => this.onThrow(...args),
validateEmptyValues: this.validateEmptyValues,
Expand Down Expand Up @@ -1154,6 +1195,7 @@ export class JSONSchemaForm extends LitElement {
#resetLoadState() {
this.#loaded = false;
this.nLoaded = 0;
this.inputs = [];
}

// Check if everything is internally rendered
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/stories/JSONSchemaForm.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Linked.args = {
},
required: ["required"],
},
conditionalRequirements: [
groups: [
{
name: "Subject Age",
properties: [["age"], ["date_of_birth"]],
Expand Down
Loading

0 comments on commit 8da7ebe

Please sign in to comment.