Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First Round of Updates from Oliver User Test #548

Merged
merged 17 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading