From 8e8af73dc07813fc2f77cac425502b5daa8b010c Mon Sep 17 00:00:00 2001 From: Steve Cassidy Date: Mon, 8 Jul 2024 18:15:28 +1000 Subject: [PATCH 1/2] remove modified message Signed-off-by: Steve Cassidy --- src/components/notebook-loader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/notebook-loader.tsx b/src/components/notebook-loader.tsx index beb590f..aa8ddc4 100644 --- a/src/components/notebook-loader.tsx +++ b/src/components/notebook-loader.tsx @@ -170,7 +170,6 @@ export const NotebookLoader = () => { onClick={(e) => { const element = e.target as HTMLInputElement; element.value = ''; }}/>) : (null)} - {notebookModified ? (

Modified

): (

Not Modified

)} {errors.length ? (
From d21f783fc4cb7d770fe83a17b5c8e9f552134ad1 Mon Sep 17 00:00:00 2001 From: Steve Cassidy Date: Mon, 8 Jul 2024 20:44:11 +1000 Subject: [PATCH 2/2] Be more liberal with extra properties in schema validation Signed-off-by: Steve Cassidy --- notebooks/Campus-Survey-Demo.json | 1046 ++++++++++++++++++++++++++++ src/components/notebook-loader.tsx | 16 +- src/components/review-panel.tsx | 16 +- src/notebook-schema.ts | 12 +- src/state/localStorage.ts | 15 +- src/state/migrateNotebook.test.ts | 25 +- src/state/store.ts | 1 + 7 files changed, 1085 insertions(+), 46 deletions(-) create mode 100644 notebooks/Campus-Survey-Demo.json diff --git a/notebooks/Campus-Survey-Demo.json b/notebooks/Campus-Survey-Demo.json new file mode 100644 index 0000000..832cd1e --- /dev/null +++ b/notebooks/Campus-Survey-Demo.json @@ -0,0 +1,1046 @@ +{ + "metadata": { + "project_status": "", + "accesses": [ + "admin", + "moderator", + "team" + ], + "forms": { + "FORM1": { + "submitActionFORM1": "Save and New", + "annotationFORM1": false, + "uncertaintyFORM1": false, + "formaccessinheritFORM1": false, + "visibleFORM1": true + }, + "FORM2": { + "submitActionFORM2": "Save and New", + "annotationFORM2": true, + "uncertaintyFORM2": true, + "formaccessinheritFORM2": false, + "visibleFORM2": true + } + }, + "sections": {}, + "meta": { + "Focus Group ID": "", + "Group": "A" + }, + "access": { + "accessFORM1": [ + "admin" + ], + "accessFORM2": [ + "admin" + ] + }, + "ispublic": false, + "isrequest": false, + "behavious": {}, + "project_lead": "Penny Crook", + "lead_institution": "Macquarie University", + "pre_description": "This is a demonstration module create for FAIMS3 focus groups held in April and May 2022.", + "filenames": "Attachment.pdf", + "notebook_version": "1.0", + "schema_version": "1.0", + "name": "Campus Survey Demo" + }, + "ui-specification": { + "fields": { + "take-gps-starting-point": { + "component-namespace": "faims-custom", + "component-name": "TakePoint", + "type-returned": "faims-pos::Location", + "component-parameters": { + "fullWidth": true, + "name": "take-gps-starting-point", + "id": "take-gps-starting-point", + "helperText": "Tap to select the starting point for the survey.", + "variant": "outlined", + "label": "Take GPS Starting Point" + }, + "validationSchema": [ + [ + "yup.object" + ], + [ + "yup.nullable" + ] + ], + "is_logic": { + "campus-zone": [ + "Zone Alpha; ", + "Zone Charlie; " + ] + }, + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "campus-zone": { + "component-namespace": "faims-custom", + "component-name": "Select", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Select your campus area from the list. (For other, use annotation icon)", + "variant": "outlined", + "required": false, + "select": true, + "InputProps": {}, + "SelectProps": {}, + "ElementProps": { + "options": [ + { + "value": "Zone Alpha; ", + "label": "Zone Alpha; " + }, + { + "value": "Zone Beta; ", + "label": "Zone Beta; " + }, + { + "value": "Zone Charlie; ", + "label": "Zone Charlie; " + }, + { + "value": "Zone Delta; ", + "label": "Zone Delta; " + }, + { + "value": "Zone Other; ", + "label": "Zone Other; " + } + ] + }, + "InputLabelProps": { + "label": "Campus Zone" + }, + "id": "campus-zone", + "name": "campus-zone" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "Other area", + "annotation": true, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + }, + "logic_select": { + "type": [ + "field", + "view" + ] + } + }, + "survey-note": { + "component-namespace": "formik-material-ui", + "component-name": "MultipleTextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Note comments about survey area here", + "variant": "outlined", + "required": false, + "multiline": true, + "InputProps": { + "type": "text", + "rows": 4 + }, + "SelectProps": {}, + "InputLabelProps": { + "label": "Survey Note" + }, + "FormHelperTextProps": {}, + "id": "survey-note", + "name": "survey-note" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "take-gps-end-point": { + "component-namespace": "faims-custom", + "component-name": "TakePoint", + "type-returned": "faims-pos::Location", + "component-parameters": { + "fullWidth": true, + "name": "take-gps-end-point", + "id": "take-gps-end-point", + "helperText": "Tap to select the end point for the survey.", + "variant": "outlined", + "label": "Take GPS End Point" + }, + "validationSchema": [ + [ + "yup.object" + ], + [ + "yup.nullable" + ] + ], + "is_logic": { + "campus-zone": [ + "Zone Alpha; ", + "Zone Charlie; " + ] + }, + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "hridFORM2": { + "component-namespace": "faims-custom", + "component-name": "TemplatedStringField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "name": "hridFORM2", + "id": "hridFORM2", + "helperText": "This is unique ID for each landscape element composed from an auto-incrementer and the element type.", + "variant": "filled", + "required": true, + "template": "Element: {{element-type}}-{{autoincrementer}}", + "InputProps": { + "type": "text", + "readOnly": true + }, + "InputLabelProps": { + "label": "Element ID" + }, + "hrid": true, + "linked": "newfieldc18d25ed", + "numberfield": 3, + "fieldselect10": "A", + "fieldselect11": "element-type", + "fieldselect12": "autoincrementer" + }, + "validationSchema": [ + [ + "yup.string" + ], + [ + "yup.required" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": true, + "uncertainty": { + "include": true, + "label": "uncertainty" + } + } + }, + "autoincrementer": { + "component-namespace": "faims-custom", + "component-name": "BasicAutoIncrementer", + "type-returned": "faims-core::String", + "component-parameters": { + "name": "autoincrementer", + "id": "autoincrementer", + "variant": "outlined", + "required": true, + "num_digits": 5, + "form_id": "FORM2SECTION1", + "label": "AutoIncrementer" + }, + "validationSchema": [ + [ + "yup.string" + ], + [ + "yup.required" + ] + ], + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": true, + "uncertainty": { + "include": true, + "label": "uncertainty" + } + } + }, + "element-type": { + "component-namespace": "faims-custom", + "component-name": "Select", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Select the type of landscape element that you see, e.g. bench seat, street lamp etc. Fill other in annotation.", + "variant": "outlined", + "required": false, + "select": true, + "InputProps": {}, + "SelectProps": {}, + "ElementProps": { + "options": [ + { + "value": "Bench seat", + "label": "Bench seat" + }, + { + "value": "Movable Chair", + "label": "Movable Chair" + }, + { + "value": "Garden bed", + "label": "Garden bed" + }, + { + "value": "Plant", + "label": "Plant" + }, + { + "value": "Rubbish bin", + "label": "Rubbish bin" + }, + { + "value": "Sculpture", + "label": "Sculpture" + }, + { + "value": "Signage", + "label": "Signage" + }, + { + "value": "Street lamp", + "label": "Street lamp" + }, + { + "value": "Table", + "label": "Table" + }, + { + "value": "Tree", + "label": "Tree" + }, + { + "value": "Other", + "label": "Other" + } + ] + }, + "InputLabelProps": { + "label": "Element Type" + }, + "id": "element-type", + "name": "element-type" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "Other", + "annotation": true, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "take-gps-point": { + "component-namespace": "faims-custom", + "component-name": "TakePoint", + "type-returned": "faims-pos::Location", + "component-parameters": { + "fullWidth": true, + "name": "take-gps-point", + "id": "take-gps-point", + "helperText": "", + "variant": "outlined", + "label": "Take GPS Point" + }, + "validationSchema": [ + [ + "yup.object" + ], + [ + "yup.nullable" + ] + ], + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "asset-number": { + "component-namespace": "formik-material-ui", + "component-name": "TextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Enter the asset number of the item, if known. In annotation, note difficulties.", + "variant": "outlined", + "required": false, + "InputProps": { + "type": "text" + }, + "SelectProps": {}, + "InputLabelProps": { + "label": "Asset number" + }, + "FormHelperTextProps": {}, + "id": "asset-number", + "name": "asset-number" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "Difficulties", + "annotation": true, + "uncertainty": { + "include": true, + "label": "Questionable" + } + } + }, + "condition": { + "component-namespace": "faims-custom", + "component-name": "RadioGroup", + "type-returned": "faims-core::String", + "component-parameters": { + "name": "condition", + "id": "condition", + "variant": "outlined", + "required": false, + "ElementProps": { + "options": [ + { + "value": "Good", + "label": "Good", + "RadioProps": { + "id": "radio-group-field-0" + } + }, + { + "value": " Satisfactory", + "label": " Satisfactory", + "RadioProps": { + "id": "radio-group-field-1" + } + }, + { + "value": " Poor", + "label": " Poor", + "RadioProps": { + "id": "radio-group-field-2" + } + }, + { + "value": " Dangerous", + "label": " Dangerous", + "RadioProps": { + "id": "radio-group-field-3" + } + }, + { + "value": " Not able to assess ", + "label": " Not able to assess ", + "RadioProps": { + "id": "radio-group-field-4" + } + } + ] + }, + "FormLabelProps": { + "children": "Condition" + }, + "FormHelperTextProps": { + "children": "Select an option for the overall condition of the landscape element. Add notes, if needed, in annotation." + } + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "1", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "Assessment note", + "annotation": true, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "element-notes": { + "component-namespace": "formik-material-ui", + "component-name": "MultipleTextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Add additional description or observations as needed.", + "variant": "outlined", + "required": false, + "multiline": true, + "InputProps": { + "type": "text", + "rows": 4 + }, + "SelectProps": {}, + "InputLabelProps": { + "label": "Element Notes" + }, + "FormHelperTextProps": {}, + "id": "element-notes", + "name": "element-notes" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": true, + "uncertainty": { + "include": true, + "label": "uncertainty" + } + } + }, + "take-photo": { + "component-namespace": "faims-custom", + "component-name": "TakePhoto", + "type-returned": "faims-attachment::Files", + "component-parameters": { + "fullWidth": true, + "name": "take-photo", + "id": "take-photo", + "helperText": "Take a photo", + "variant": "outlined", + "label": "Take Photo" + }, + "validationSchema": [ + [ + "yup.object" + ], + [ + "yup.nullable" + ] + ], + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "nearest-building": { + "component-namespace": "formik-material-ui", + "component-name": "TextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Make a note of the nearest identifiable building. Check uncertain in the field annotation if uncertain.", + "variant": "outlined", + "required": false, + "InputProps": {}, + "SelectProps": {}, + "InputLabelProps": { + "label": "Nearest building" + }, + "FormHelperTextProps": {}, + "id": "nearest-building", + "name": "nearest-building" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": true, + "label": "Uncertain" + } + } + }, + "hridFORM1": { + "component-namespace": "faims-custom", + "component-name": "TemplatedStringField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "name": "hridFORM1", + "id": "hridFORM1", + "helperText": "A read-only field composited from campus-zone and nickname to make a custom ID.", + "variant": "filled", + "required": true, + "template": "Survey Area: {{campus-zone}} {{survey-area-nickname}}", + "InputProps": { + "type": "text", + "readOnly": true + }, + "InputLabelProps": { + "label": "Survey Area ID" + }, + "hrid": true, + "numberfield": 2, + "fieldselect10": "campus-zone", + "fieldselect11": "survey-area-nickname", + "linked": "newfield757083da" + }, + "validationSchema": [ + [ + "yup.string" + ], + [ + "yup.required" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "landscape-elements": { + "component-namespace": "faims-custom", + "component-name": "RelatedRecordSelector", + "type-returned": "faims-core::Relationship", + "component-parameters": { + "fullWidth": true, + "helperText": "Associate/record new street furniture with this survey area", + "variant": "outlined", + "required": false, + "related_type": "FORM2", + "relation_type": "faims-core::Child", + "InputProps": { + "type": "text" + }, + "multiple": true, + "SelectProps": {}, + "InputLabelProps": { + "label": "Landscape Elements" + }, + "FormHelperTextProps": {}, + "id": "landscape-elements", + "name": "landscape-elements" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": [], + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "survey-area-nickname": { + "component-namespace": "formik-material-ui", + "component-name": "TextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Give a memorable name to your survey area.", + "variant": "outlined", + "required": false, + "InputProps": { + "type": "text" + }, + "SelectProps": {}, + "InputLabelProps": { + "label": "Survey Area Nickname" + }, + "FormHelperTextProps": {}, + "id": "survey-area-nickname", + "name": "survey-area-nickname" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "take-photo-2": { + "component-namespace": "faims-custom", + "component-name": "TakePhoto", + "type-returned": "faims-attachment::Files", + "component-parameters": { + "fullWidth": true, + "name": "take-photo-2", + "id": "take-photo-2", + "helperText": "Take a photo", + "variant": "outlined", + "label": "Take Photo" + }, + "validationSchema": [ + [ + "yup.object" + ], + [ + "yup.nullable" + ] + ], + "initialValue": null, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "journal": { + "component-namespace": "formik-material-ui", + "component-name": "MultipleTextField", + "type-returned": "faims-core::String", + "component-parameters": { + "fullWidth": true, + "helperText": "Journal entries as needed", + "variant": "outlined", + "required": false, + "multiline": true, + "InputProps": { + "type": "text", + "rows": 4 + }, + "SelectProps": {}, + "InputLabelProps": { + "label": "Journal" + }, + "FormHelperTextProps": {}, + "id": "journal", + "name": "journal" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "checkbox": { + "component-namespace": "faims-custom", + "component-name": "Checkbox", + "type-returned": "faims-core::Bool", + "component-parameters": { + "name": "checkbox", + "id": "checkbox", + "required": false, + "type": "checkbox", + "FormControlLabelProps": { + "label": "Safety Hazard" + }, + "FormHelperTextProps": { + "children": "Selecting this box will alert maintenance (eventually)" + } + }, + "validationSchema": [ + [ + "yup.bool" + ] + ], + "initialValue": false, + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "draw-bounding-box-around-survey-area": { + "component-namespace": "mapping-plugin", + "component-name": "MapFormField", + "type-returned": "faims-core::JSON", + "component-parameters": { + "name": "draw-bounding-box-around-survey-area", + "id": "draw-bounding-box-around-survey-area", + "variant": "outlined", + "required": false, + "featureType": "Polygon", + "zoom": "", + "label": "Draw bounding box around survey area", + "FormLabelProps": { + "children": "Survey Area Polygon" + } + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": "1", + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": false, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + }, + "related-survey-area": { + "component-namespace": "faims-custom", + "component-name": "RelatedRecordSelector", + "type-returned": "faims-core::Relationship", + "component-parameters": { + "fullWidth": true, + "helperText": "Select or Add new related survey area", + "variant": "outlined", + "required": false, + "related_type": "FORM1", + "relation_type": "faims-core::Linked", + "InputProps": { + "type": "text" + }, + "multiple": true, + "SelectProps": {}, + "InputLabelProps": { + "label": "Related Survey Area" + }, + "FormHelperTextProps": {}, + "id": "related-survey-area", + "name": "related-survey-area", + "relation_linked_vocabPair": [ + [ + "performe after", + "performed before" + ], + [ + "is similiar to", + "is similiar to" + ], + [ + "overlaps with", + "is overlapped by" + ] + ], + "related_type_label": "Area", + "current_form": "FORM1", + "current_form_label": "Area" + }, + "validationSchema": [ + [ + "yup.string" + ] + ], + "initialValue": [], + "access": [ + "admin" + ], + "meta": { + "annotation_label": "annotation", + "annotation": true, + "uncertainty": { + "include": false, + "label": "uncertainty" + } + } + } + }, + "fviews": { + "FORM1SECTION1": { + "fields": [ + "hridFORM1", + "campus-zone", + "survey-area-nickname", + "draw-bounding-box-around-survey-area", + "take-gps-starting-point", + "take-gps-end-point", + "survey-note", + "landscape-elements", + "related-survey-area" + ], + "uidesign": "form", + "label": "Survey Details", + "description": "Here you will describe the survey session." + }, + "FORM2SECTION1": { + "fields": [ + "hridFORM2", + "autoincrementer", + "asset-number", + "element-type", + "take-gps-point", + "nearest-building", + "checkbox", + "condition", + "take-photo", + "element-notes" + ], + "uidesign": "form", + "label": "Description" + }, + "FORM1SECTION2": { + "fields": [ + "take-photo-2", + "journal" + ], + "uidesign": "form", + "label": "Journal", + "is_logic": { + "campus-zone": [ + "Zone Beta; ", + "Zone Charlie; " + ] + } + } + }, + "viewsets": { + "FORM1": { + "views": [ + "FORM1SECTION1", + "FORM1SECTION2" + ], + "label": "Survey Area" + }, + "FORM2": { + "views": [ + "FORM2SECTION1" + ], + "label": "Landscape Element" + } + }, + "visible_types": [ + "FORM1", + "FORM2" + ] + } +} \ No newline at end of file diff --git a/src/components/notebook-loader.tsx b/src/components/notebook-loader.tsx index aa8ddc4..4d282c5 100644 --- a/src/components/notebook-loader.tsx +++ b/src/components/notebook-loader.tsx @@ -21,8 +21,8 @@ import { useAppDispatch, useAppSelector } from '../state/hooks'; import { useCallback, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import CloseIcon from '@mui/icons-material/Close'; -import { slugify } from '../state/uiSpec-reducer'; import { ValidationError, migrateNotebook } from '../state/migrateNotebook'; +import { downloadNotebook } from '../state/localStorage'; const VisuallyHiddenInput = styled('input')({ clip: 'rect(0 0 0 0)', @@ -141,18 +141,6 @@ export const NotebookLoader = () => { } }; - const downloadNotebook = () => { - const element = document.createElement("a"); - const file = new Blob([JSON.stringify(notebook, null, 2)], { type: 'application/json' }); - element.href = URL.createObjectURL(file); - const name = slugify(notebook.metadata.name as string); - element.download = `${name}.json`; - document.body.appendChild(element); - element.click(); - setOpen(false); - dispatch({ type: "modifiedStatus/resetFlag", payload: false}); - }; - return ( @@ -216,7 +204,7 @@ export const NotebookLoader = () => { {alertMsgContext} - + {isUpload ? (
diff --git a/src/notebook-schema.ts b/src/notebook-schema.ts index eca5489..15d36f1 100644 --- a/src/notebook-schema.ts +++ b/src/notebook-schema.ts @@ -23,8 +23,7 @@ export const schema = { notebook_version: { type: "string" }, schema_version: { type: "string" }, }, - required: ["name", "notebook_version", "schema_version"], - additionalProperties: {}, + required: ["name"], }, NotebookUISpec: { type: "object", @@ -124,7 +123,6 @@ export const schema = { label: {type: "string",}, }, required: ["include", "label"], - additionalProperties: false, }, ], }, @@ -149,7 +147,6 @@ export const schema = { "type-returned", "component-parameters", ], - additionalProperties: false, }, ComponentParameters: { type: "object", @@ -177,12 +174,10 @@ export const schema = { RadioProps: {}, }, required: ["value", "label"], - additionalProperties: false, }, }, optiontree: {}, }, - additionalProperties: true, }, InputLabelProps: { type: "object", @@ -190,7 +185,6 @@ export const schema = { label: {type: "string",}, }, required: ["label"], - additionalProperties: true, }, InputProps: { type: "object", @@ -210,7 +204,6 @@ export const schema = { type: "string", }, }, - additionalProperties: false, }, FormHelperTextProps: { type: "object", @@ -218,7 +211,6 @@ export const schema = { children: {type: "string", }, }, - additionalProperties: false, }, FormControlLabelProps: { type: "object", @@ -226,7 +218,6 @@ export const schema = { label: {type: "string",}, }, required: ["label"], - additionalProperties: false, }, initialValue: {}, related_type: {type: "string",}, @@ -262,7 +253,6 @@ export const schema = { type: {type: "string",}, valuetype: {type: "string",}, }, - additionalProperties: true, }, ValidationSchemaElement: { type: "array", diff --git a/src/state/localStorage.ts b/src/state/localStorage.ts index 17887a9..666c622 100644 --- a/src/state/localStorage.ts +++ b/src/state/localStorage.ts @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { AppState } from './initial'; +import { AppState, Notebook } from './initial'; +import { slugify } from './uiSpec-reducer'; // The following functions are inspired by Dan Abramov's lesson on persisting redux state to localStorage, // see https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage. @@ -34,4 +35,14 @@ export const saveState = (state: AppState) => { catch (error: unknown) { console.log(error); } -}; \ No newline at end of file +}; + +export const downloadNotebook = (notebook: Notebook) => { + const element = document.createElement("a"); + const file = new Blob([JSON.stringify(notebook, null, 2)], { type: 'application/json' }); + element.href = URL.createObjectURL(file); + const name = slugify(notebook.metadata.name as string); + element.download = `${name}.json`; + document.body.appendChild(element); + element.click(); +}; diff --git a/src/state/migrateNotebook.test.ts b/src/state/migrateNotebook.test.ts index a06bb13..c3721f0 100644 --- a/src/state/migrateNotebook.test.ts +++ b/src/state/migrateNotebook.test.ts @@ -16,6 +16,10 @@ import {describe, expect, test } from 'vitest'; import { migrateNotebook, validateNotebook } from './migrateNotebook'; import { sampleNotebook } from '../test-notebook'; +import f3demo from '../../notebooks/FAIMS3-Beta-Demo-Notebook.json'; +import campusDemo from '../../notebooks/Campus-Survey-Demo.json'; +import sampleNB from '../../notebooks/sample_notebook.json'; + describe('Migrate Notebook Tests', () => { @@ -88,8 +92,6 @@ describe('Migrate Notebook Tests', () => { } }); - - test('update form descriptions', () => { const migrated = migrateNotebook(sampleNotebook); @@ -102,10 +104,6 @@ describe('Migrate Notebook Tests', () => { }); - - - - test('not losing properties', () => { const migrated = migrateNotebook(sampleNotebook); @@ -114,4 +112,19 @@ describe('Migrate Notebook Tests', () => { expect(Object.getOwnPropertyNames(fields)).toContain(fieldName); }); }); + + + test('validate sample notebooks', () => { + const migratedF3 = migrateNotebook(f3demo); + // should not throw an exception + expect(migratedF3.metadata.name).toBe('Faims3 Beta Demo Notebook'); + + const migratedCD = migrateNotebook(campusDemo); + expect(migratedCD.metadata.name).toBe('Campus Survey Demo'); + + const migratedS = migrateNotebook(sampleNB); + expect(migratedS.metadata.name).toBe('Blue Mountains Survey'); + + + }) }) \ No newline at end of file diff --git a/src/state/store.ts b/src/state/store.ts index 96eff35..e95ca7b 100644 --- a/src/state/store.ts +++ b/src/state/store.ts @@ -29,6 +29,7 @@ const loggerMiddleware: Middleware = storeAPI => next => actio console.log('next state', storeAPI.getState()) } + export const store: ToolkitStore = configureStore({ reducer: { notebook: combineReducers({