From 07f50b9da47b5cfdcc384723a79ec563ae6b12c2 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 5 Aug 2024 07:30:02 -0700 Subject: [PATCH 01/23] Working author logic for adding and deleting authors --- .../src/components/upload/AuthorForm.tsx | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/frontend/src/components/upload/AuthorForm.tsx b/packages/frontend/src/components/upload/AuthorForm.tsx index cb12c30..82d1590 100644 --- a/packages/frontend/src/components/upload/AuthorForm.tsx +++ b/packages/frontend/src/components/upload/AuthorForm.tsx @@ -38,35 +38,43 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata setAuthors([...authors, { name: '', identifier: '' }]); }; + const handleSubmit = () => { for (const author of authors) { - console.log("handling author:", author); + console.log("handling author:", author); - const name = author['name']; - const identifier = author['identifier']; - const oldName = ("oldName" in author) ? author["oldName"] : ""; - const existed = ("oldName" in author); + const name = author['name']; + const identifier = author['identifier']; + const oldName = ("oldName" in author) ? author["oldName"] : ""; + const existed = ("oldName" in author); - if (name === "") continue; // skip if no name + if (name === "") continue; // skip if no name - if (!existed) { // no old name means can just add the two new fields - if (identifier === "") jsPsychMetadata.setAuthor({ "name": name}); - else jsPsychMetadata.setAuthor({ "name": name, "identifier": identifier}); - } - else { // 1. need to check if name changed to delete 2. need to hanlde oldfields - // const addAuthor = "this is where need to process fields and iterate through them checking if empty or not"; + if (!existed) { // no old name means can just add the two new fields + if (identifier === "") { + jsPsychMetadata.setAuthor({ "name": name }); + } else { + jsPsychMetadata.setAuthor({ "name": name, "identifier": identifier }); + } + } else { + // 1. need to check if name changed to delete 2. need to handle old fields + const authorWithIndex = author as AuthorFields & Record; // typecasting + for (const key in authorWithIndex) { + if (authorWithIndex[key] === "") { + delete authorWithIndex[key]; + } + } - if (oldName === name){ // case where need handle old Fields - // add author - } else { // case where delete - jsPsychMetadata.deleteAuthor(author["oldName"] as string); // weird type casting where just for this need to break - // add author + if (oldName === name) { // case where need to handle old fields + // add author + } else { // case where delete + jsPsychMetadata.deleteAuthor(author["oldName"] as string); // weird type casting where just for this need to break + // add author + } + jsPsychMetadata.setAuthor(authorWithIndex); } - } - } - // iterate thrugh authors figuring out whether have to delete or not based on old name and setting them to equal whatever - // setting is okay because will overwrite the old script + handleScreenChange('data', 'skip'); updateMetadataString(); } From b42e05fde6451aa25653ce9c1e62164b2912df32 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 5 Aug 2024 07:46:36 -0700 Subject: [PATCH 02/23] Fixed up authors logic with single author and imported typescript --- package-lock.json | 12 +++++------ .../src/components/upload/AuthorForm.tsx | 21 +++++++++---------- packages/metadata/package.json | 2 +- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15e1971..ada3cf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8677,10 +8677,10 @@ }, "packages/cli": { "name": "@jspsych/metadata-cli", - "version": "0.0.1", + "version": "0.0.2", "dependencies": { "@inquirer/prompts": "^5.1.2", - "@jspsych/metadata": "^0.0.1", + "@jspsych/metadata": "^0.0.2", "fuzzy": "^0.1.3", "inquirer": "^10.0.1", "yargs": "^17.7.2" @@ -8690,9 +8690,9 @@ } }, "packages/frontend": { - "version": "0.0.0", + "version": "0.0.1", "dependencies": { - "@jspsych/metadata": "^0.0.1", + "@jspsych/metadata": "^0.0.2", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -9195,7 +9195,7 @@ }, "packages/metadata": { "name": "@jspsych/metadata", - "version": "0.0.1", + "version": "0.0.2", "license": "MIT", "dependencies": { "csv-parse": "^5.5.6" @@ -9204,7 +9204,7 @@ "@types/jest": "^29.5.12", "husky": "^9.0.11", "ts-jest": "^29.1.4", - "typescript": "^5.5.3" + "typescript": "^5.5.4" } } } diff --git a/packages/frontend/src/components/upload/AuthorForm.tsx b/packages/frontend/src/components/upload/AuthorForm.tsx index 82d1590..62bbec4 100644 --- a/packages/frontend/src/components/upload/AuthorForm.tsx +++ b/packages/frontend/src/components/upload/AuthorForm.tsx @@ -45,7 +45,7 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata const name = author['name']; const identifier = author['identifier']; - const oldName = ("oldName" in author) ? author["oldName"] : ""; + const oldName: string = ("oldName" in author) ? author["oldName"] as string: ""; const existed = ("oldName" in author); if (name === "") continue; // skip if no name @@ -57,24 +57,22 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata jsPsychMetadata.setAuthor({ "name": name, "identifier": identifier }); } } else { - // 1. need to check if name changed to delete 2. need to handle old fields + // cleaning up old fields const authorWithIndex = author as AuthorFields & Record; // typecasting for (const key in authorWithIndex) { - if (authorWithIndex[key] === "") { - delete authorWithIndex[key]; + if (authorWithIndex[key] === "" || key === "oldName") { + delete authorWithIndex[key]; } } - if (oldName === name) { // case where need to handle old fields - // add author - } else { // case where delete - jsPsychMetadata.deleteAuthor(author["oldName"] as string); // weird type casting where just for this need to break - // add author + if (oldName !== name) { + jsPsychMetadata.deleteAuthor(oldName); // weird type casting where just for this need to break } + jsPsychMetadata.setAuthor(authorWithIndex); } } - + handleScreenChange('data', 'skip'); updateMetadataString(); } @@ -82,10 +80,11 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata return (

Authors

+

The name field is required and any author missing the name field will not be added. This can be edited and authors can be added later.

{authors.map((author, index) => (
-
- - -
From e164e7fa20cd5918f2a395810554d3b1a31437cd Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 5 Aug 2024 07:55:50 -0700 Subject: [PATCH 04/23] Basic authorform styling to make legibility easier --- packages/frontend/src/App.css | 13 +++- .../src/components/upload/AuthorForm.tsx | 68 ++++++++++--------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index f6c2b3f..ebfdfad 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -158,17 +158,26 @@ html, body { /* .promptFormSurvey label { } */ -.promptFormSurvey input { +.promptFormSurvey input, .authorInput { margin-left: 10px; + padding: 5px; border: 1px solid #ccc; border-radius: 4px; } .promptFormSubmit { width: fit-content; +} + +.authorFormAddAuthors, .authorFormSubmit { + margin: 15px; +} +.authorLabel { + margin-left: 10px; } .pageDescription { margin-bottom: 32px; -} \ No newline at end of file +} + diff --git a/packages/frontend/src/components/upload/AuthorForm.tsx b/packages/frontend/src/components/upload/AuthorForm.tsx index 62bbec4..10d25e3 100644 --- a/packages/frontend/src/components/upload/AuthorForm.tsx +++ b/packages/frontend/src/components/upload/AuthorForm.tsx @@ -41,36 +41,36 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata const handleSubmit = () => { for (const author of authors) { - console.log("handling author:", author); - - const name = author['name']; - const identifier = author['identifier']; - const oldName: string = ("oldName" in author) ? author["oldName"] as string: ""; - const existed = ("oldName" in author); - - if (name === "") continue; // skip if no name - - if (!existed) { // no old name means can just add the two new fields - if (identifier === "") { - jsPsychMetadata.setAuthor({ "name": name }); - } else { - jsPsychMetadata.setAuthor({ "name": name, "identifier": identifier }); - } - } else { - // cleaning up old fields - const authorWithIndex = author as AuthorFields & Record; // typecasting - for (const key in authorWithIndex) { - if (authorWithIndex[key] === "" || key === "oldName") { - delete authorWithIndex[key]; - } - } - - if (oldName !== name) { - jsPsychMetadata.deleteAuthor(oldName); // weird type casting where just for this need to break - } - - jsPsychMetadata.setAuthor(authorWithIndex); + console.log("handling author:", author); + + const name = author['name']; + const identifier = author['identifier']; + const oldName: string = ("oldName" in author) ? author["oldName"] as string: ""; + const existed = ("oldName" in author); + + if (name === "") continue; // skip if no name + + if (!existed) { // no old name means can just add the two new fields + if (identifier === "") { + jsPsychMetadata.setAuthor({ "name": name }); + } else { + jsPsychMetadata.setAuthor({ "name": name, "identifier": identifier }); + } + } else { + // cleaning up old fields + const authorWithIndex = author as AuthorFields & Record; // typecasting + for (const key in authorWithIndex) { + if (authorWithIndex[key] === "" || key === "oldName") { + delete authorWithIndex[key]; + } } + + if (oldName !== name) { + jsPsychMetadata.deleteAuthor(oldName); // weird type casting where just for this need to break + } + + jsPsychMetadata.setAuthor(authorWithIndex); + } } handleScreenChange('data', 'skip'); @@ -83,29 +83,31 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata

The name field is required and any author missing the name field will not be added. This can be edited and authors can be added later.

{authors.map((author, index) => (
-
))} - - +
); }; From 4973a1ae277517ba52a18ada88e927f794646cbe Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 5 Aug 2024 18:37:48 -0700 Subject: [PATCH 05/23] Pushing changes filtering fields for preview --- packages/frontend/src/App.tsx | 2 +- packages/frontend/src/components/Preview.tsx | 28 +++++++++++- packages/metadata/src/index.ts | 48 ++++++++++++++++++-- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index d9764c4..094b71d 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -8,7 +8,7 @@ import './App.css' function App() { const [jsPsychMetadata] = useState(new JsPsychMetadata()); // metadata objct const [ metadataString, setMetadataString ] = useState(JSON.stringify(jsPsychMetadata.getMetadata(), null, 2)); // this is the metadata string that willl keep track of metadata - const [ page, setPage ] = useState('upload'); // page logic + const [ page, setPage ] = useState('viewOptions'); // page logic, change back to upload when done working with preview page // const [ fileList, setFileList ] = useState([]); -> this allows to download and save // whenever updates will just call pretty version diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 21456f1..7154b34 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -1,18 +1,42 @@ import JsPsychMetadata from '@jspsych/metadata'; +import { useState } from 'react'; interface PreviewProps { jsPsychMetadata: JsPsychMetadata; metadataString: string; } -const Preview: React.FC = ( { metadataString } ) => { +const Preview: React.FC = ( { jsPsychMetadata, metadataString } ) => { + const [ metadataObject, setMetadataObject ] = useState<{ [key: string]: any }>(jsPsychMetadata.getUserMetadataFields()); // fields + const [ authorsList, setAuthorsList ] = useState(jsPsychMetadata.getAuthorList()); // authors + const [ variablesList, setVariablesList ] = useState(jsPsychMetadata.getVariableList()); // variables + + const updateState = () => { + setMetadataObject(jsPsychMetadata.getUserMetadataFields()); + setAuthorsList(jsPsychMetadata.getAuthorList()); + setVariablesList(jsPsychMetadata.getVariableList()); + } + + const fields = () => { + var res = ""; + + for (const key in metadataObject){ + res += key + ": " + metadataObject[key] + "\n"; + } + + return res; + } + return (

Metadata preview

-          {metadataString}
+          {fields()}
         
+ {/*
+          {metadataString}
+        
*/}
) diff --git a/packages/metadata/src/index.ts b/packages/metadata/src/index.ts index 3cf55ed..7dff2ea 100644 --- a/packages/metadata/src/index.ts +++ b/packages/metadata/src/index.ts @@ -45,13 +45,13 @@ export default class JsPsychMetadata { private pluginCache: PluginCache; /** - * Initializes a set that contains the fields that are to be ignored, so can help with later + * Initializes a set that contains the variable fields that are to be ignored, so can help with later * logic when generating data. * * @private * @type {*} */ - private ignored_fields = new Set([ + private ignored_variables = new Set([ "trial_type", "trial_index", "time_elapsed", @@ -134,7 +134,8 @@ export default class JsPsychMetadata { /** * Returns the final Metadata in a single javascript object. Bundles together the author and variables - * together in a list rather than object compliant with Psych-DS standards. + * together in a list rather than object compliant with Psych-DS standards. Seems that javascript get + * are implictly called. * * @returns {{}} - Final Metadata object */ @@ -146,6 +147,33 @@ export default class JsPsychMetadata { return res; } + getUserMetadataFields (): Record { + const res = {}; + + const ignored_fields = new Set(["schemaVersion", "@type", "@context", "author", "variableMeasured"]); // getMetdata() impliclty called + + for (const key in this.metadata){ + if (!(ignored_fields.has(key))){ + res[key] = this.metadata[key]; + } + } + + return res; + } + + /** + * Returns the variable fields while excluding the authors and variables.` + * + * @returns {{}} - Final Metadata object + */ + getMetadataFields(): {} { + const res = this.metadata; + delete res["author"]; + delete res["variableMeasured"]; + + return res; + } + /** * Generates the default descriptions for extension_type and extension_version in metadata. Should be called after * default metadata is generated, and only should be called once. @@ -162,7 +190,7 @@ export default class JsPsychMetadata { */ setAuthor(fields: AuthorFields): void { this.authors.setAuthor(fields); // Assuming `authors` is an instance of the AuthorsMap class - } + } /** * Method that fetches an author object allowing user to update (in existing workflow should not be necessary). @@ -227,6 +255,16 @@ export default class JsPsychMetadata { return this.variables.getVariable(name); } + + /** + * Returns a list of the variables defined in the metadata. + * + * @returns {{}[]} - Authors + */ + getVariableList(): ({})[] { + return this.variables.getList(); + } + /** * Allows you to check if the name of the variable exists in variablesMap. * @@ -414,7 +452,7 @@ export default class JsPsychMetadata { } } - if (this.ignored_fields.has(variable)) this.updateFields(variable, value, type); + if (this.ignored_variables.has(variable)) this.updateFields(variable, value, type); else { await this.generateMetadata(variable, value, pluginType, version); From ce4b4d50292ed7efe002274f7fb9b39b599570c8 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 5 Aug 2024 18:41:21 -0700 Subject: [PATCH 06/23] Setting up authors and variabless --- packages/frontend/src/components/Preview.tsx | 31 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 7154b34..82b82bb 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -27,6 +27,28 @@ const Preview: React.FC = ( { jsPsychMetadata, metadataString } ) return res; } + const authors = () => { + var res = ""; + + for (const key in authorsList){ + res += authorsList[key] + "\n"; + } + + return res; + } + + // need to build out logic for how to increment thorugh these + const variables = () => { + var res = ""; + + // likely go through the fields and pick some that we want, and others that don't want + for (const key in variablesList){ + res += variablesList[key] + "\n"; + } + + return res; + } + return (

Metadata preview

@@ -34,9 +56,12 @@ const Preview: React.FC = ( { jsPsychMetadata, metadataString } )
           {fields()}
         
- {/*
-          {metadataString}
-        
*/} +
+          {authors()}
+        
+
+          {variables()}
+        
) From 1117df78b52db145b6ddfc1bc2f7c26a031d9807 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Tue, 6 Aug 2024 18:28:06 -0700 Subject: [PATCH 07/23] Building out page logic and replacing preview fields with ability to edit and remove edit existing fields --- packages/frontend/src/App.tsx | 4 ++-- packages/frontend/src/components/Preview.tsx | 5 ++++- packages/frontend/src/pages/Options.tsx | 6 ++++-- packages/frontend/src/pages/ViewOptions.tsx | 5 +++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 094b71d..c0a6506 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -8,7 +8,7 @@ import './App.css' function App() { const [jsPsychMetadata] = useState(new JsPsychMetadata()); // metadata objct const [ metadataString, setMetadataString ] = useState(JSON.stringify(jsPsychMetadata.getMetadata(), null, 2)); // this is the metadata string that willl keep track of metadata - const [ page, setPage ] = useState('viewOptions'); // page logic, change back to upload when done working with preview page + const [ page, setPage ] = useState('upload'); // page logic, change back to upload when done working with preview page // const [ fileList, setFileList ] = useState([]); -> this allows to download and save // whenever updates will just call pretty version @@ -22,7 +22,7 @@ function App() { case 'upload': return ; // NEED TO PASS IN UPDATE METADATA SO THAT WILL UPDATE STRING WHEN LOADING case 'viewOptions': - return ; // NEED TO PASS SETPAGE ELEMENT + return ; // NEED TO PASS SETPAGE ELEMENT default: console.warn("uncaught page render:", page); return ; // NEED TO PASS IN UPDATE METADATA SO THAT WILL UPDATE STRING WHEN LOADING diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 82b82bb..766c80a 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -43,7 +43,7 @@ const Preview: React.FC = ( { jsPsychMetadata, metadataString } ) // likely go through the fields and pick some that we want, and others that don't want for (const key in variablesList){ - res += variablesList[key] + "\n"; + res += JSON.stringify(variablesList[key]) + "\n"; } return res; @@ -53,12 +53,15 @@ const Preview: React.FC = ( { jsPsychMetadata, metadataString } )

Metadata preview

+

Fields

           {fields()}
         
+

Authors

           {authors()}
         
+

Variables

           {variables()}
         
diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 5e0154f..52ca0cb 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -8,9 +8,10 @@ import JsPsychMetadata, { AuthorFields, VariableFields } from '@jspsych/metadata interface OptionsProps { jsPsychMetadata: JsPsychMetadata; updateMetadataString: () => void; + setPage: (s: string) => void; } -const Options: React.FC = ( { jsPsychMetadata, updateMetadataString } ) => { +const Options: React.FC = ( { jsPsychMetadata, updateMetadataString, setPage } ) => { const [isPopupOpen, setIsPopupOpen] = useState(false); const [popupType, setPopupType] = useState(''); const [popupData, setPopupData] = useState({}); // State to hold popup-specific data @@ -96,7 +97,8 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin

It is highly advised you enter author information and edit the title

- + + {isPopupOpen && renderPopup()} {/* */} diff --git a/packages/frontend/src/pages/ViewOptions.tsx b/packages/frontend/src/pages/ViewOptions.tsx index 5876799..bc49d15 100644 --- a/packages/frontend/src/pages/ViewOptions.tsx +++ b/packages/frontend/src/pages/ViewOptions.tsx @@ -6,9 +6,10 @@ interface ViewOptionsProps { jsPsychMetadata: JsPsychMetadata; metadataString: string; updateMetadataString: () => void; + setPage: (s: string) => void; } -const ViewOptions: React.FC = ( {jsPsychMetadata, metadataString, updateMetadataString } ) => { +const ViewOptions: React.FC = ( {jsPsychMetadata, metadataString, updateMetadataString, setPage } ) => { return ( <> @@ -17,7 +18,7 @@ const ViewOptions: React.FC = ( {jsPsychMetadata, metadataStri
- +
From 188a474bedaf5d883fae96385216ccc08741d623 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 11:34:00 -0700 Subject: [PATCH 08/23] adding button to upload additional data files --- packages/frontend/src/App.tsx | 2 ++ packages/frontend/src/components/upload/AuthorForm.tsx | 2 +- packages/frontend/src/pages/Options.tsx | 2 +- packages/frontend/src/pages/Upload.tsx | 9 +++++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index c0a6506..10ee93c 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -21,6 +21,8 @@ function App() { switch (page) { case 'upload': return ; // NEED TO PASS IN UPDATE METADATA SO THAT WILL UPDATE STRING WHEN LOADING + case 'upload-data': + return ; // NEED TO PASS IN UPDATE METADATA SO THAT WILL UPDATE STRING WHEN LOADING case 'viewOptions': return ; // NEED TO PASS SETPAGE ELEMENT default: diff --git a/packages/frontend/src/components/upload/AuthorForm.tsx b/packages/frontend/src/components/upload/AuthorForm.tsx index 10d25e3..de66cdf 100644 --- a/packages/frontend/src/components/upload/AuthorForm.tsx +++ b/packages/frontend/src/components/upload/AuthorForm.tsx @@ -66,7 +66,7 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata } if (oldName !== name) { - jsPsychMetadata.deleteAuthor(oldName); // weird type casting where just for this need to break + jsPsychMetadata.deleteAuthor(oldName); } jsPsychMetadata.setAuthor(authorWithIndex); diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 52ca0cb..bc70a0e 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -98,7 +98,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin - + {isPopupOpen && renderPopup()} {/* */} diff --git a/packages/frontend/src/pages/Upload.tsx b/packages/frontend/src/pages/Upload.tsx index c383db6..29f1e3d 100644 --- a/packages/frontend/src/pages/Upload.tsx +++ b/packages/frontend/src/pages/Upload.tsx @@ -9,13 +9,14 @@ interface UploadProps { jsPsychMetadata: JsPsychMetadata; setPage: (s: string) => void; updateMetadataString: () => void; + startScreen?: string; // optional parameter to determien which of upload screens begins at } -const Upload: React.FC = ( { jsPsychMetadata, setPage, updateMetadataString }) => { - const [ dataScreen, setDataScreen] = useState('metadata'); +const Upload: React.FC = ( { jsPsychMetadata, setPage, updateMetadataString, startScreen }) => { + const [ dataScreen, setDataScreen] = useState(startScreen ? startScreen: 'metadata'); const [ buttonText, setButtonText ] = useState('Skip'); - const [ pageNumber, setPageNumber ] = useState(1); - + const [ pageNumber, setPageNumber ] = useState(startScreen ? 4 : 1); // bad way to write, but fixes immediate logic of displayData + const handleScreenChange = (newPage?: string, newButtonText?: string) => { if (newPage !== undefined){ setDataScreen(newPage); From 1de3c5ed6865960650986ed01831fee0ba0f7a04 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 12:54:29 -0700 Subject: [PATCH 09/23] Initial list items screen for preview completed --- .../frontend/src/components/ListItems.tsx | 160 ++++++++++++++++++ packages/frontend/src/components/Preview.tsx | 90 +++++++++- packages/frontend/src/pages/Options.tsx | 3 +- packages/frontend/src/pages/ViewOptions.tsx | 2 +- 4 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 packages/frontend/src/components/ListItems.tsx diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx new file mode 100644 index 0000000..477f49d --- /dev/null +++ b/packages/frontend/src/components/ListItems.tsx @@ -0,0 +1,160 @@ +import JsPsychMetadata from '@jspsych/metadata'; +import React, { useState } from 'react'; +import Trash from '../assets/trash.svg' + +type ListItemsProps = { + jsPsychMetadata: JsPsychMetadata; + setPopupType: (type: string) => void; // Update setPopupType to accept optional data + setPopupData: (data: any) => void; + updateMetadataString: () => void; + openPopup: (type: string, data?: any) => void; + closePopup: () => void; // shoudl be passed to the other item +} + +type Author = { + /** The type of the author. */ + "@type"?: string; + /** The name of the author. (required) */ + name: string; + /** The given name of the author. */ + givenName?: string; + /** The family name of the author. */ + familyName?: string; + /** The identifier that distinguishes the author across datasets (URL). */ + identifier?: string; +} + +type VariableMeasured = { + "@type": string; + name: string; // required + description: string | Record; + value: string | boolean | number; + identifier?: string; // identifier that distinguishes across dataset (URL) + minValue?: number; + maxValue?: number; + levels?: string[]; // array of string values + levelsOrdered?: boolean; + na?: boolean; + naValue?: string; + alternateName?: string; + privacy?: string; +} + +type Metadata = { + name: string; + schemaVersion: string; + "@context": string; + "@type": string; + description: string; + author: Author[]; + variableMeasured: VariableMeasured[]; + [key: string]: any; +} + +const ListItems: React.FC = ({ jsPsychMetadata, setPopupType, setPopupData, updateMetadataString, openPopup, closePopup }) => { + const generateButtons = (metadata: Metadata) => { + const res = []; + for (const key in metadata) { + const value = metadata[key]; + + if (key === "variableMeasured"){ + for (const variable_key in value){ + const variable = value[variable_key]; + + res.push( +
+ + +
+ ); + } + } else if (key === "author"){ + for (const author_key in value){ + const author = value[author_key]; + let author_typing: {}; + if (typeof author === "string") author_typing = { "name": author }; // fix name only issues + + res.push( +
+ + +
+ ); + } + } else { + res.push( +
+ + +
+ ); + } + } + + return res; + } + + const [buttons, setbuttons] = useState(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); + + const handleDelete = (name: string, type: string) => { + switch (type) { + case 'variable': + jsPsychMetadata.deleteVariable(name); + break; + case 'author': + jsPsychMetadata.deleteAuthor(name); + break + case 'field': + jsPsychMetadata.deleteMetadataField(name); + break + } + + // updates the UI -> need update String + setbuttons(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); + updateMetadataString(); + } + + + return ( +
+
+

This is the listeItems page

+
+ {buttons} +
+
+
+ ) +} + +export default ListItems; diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 766c80a..c738a09 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -1,16 +1,100 @@ -import JsPsychMetadata from '@jspsych/metadata'; +import JsPsychMetadata, {AuthorFields, VariableFields } from '@jspsych/metadata'; import { useState } from 'react'; +import FieldPopup, { FieldFormData } from './popups/FieldPopup'; +import AuthorPopup, { AuthorFormData } from './popups/AuthorPopup'; +import VariablePopup, { VariableFormData } from './popups/VariablePopup'; +import ListPopup from '../components/popups/ListPopup'; +import ListItems from './ListItems'; interface PreviewProps { jsPsychMetadata: JsPsychMetadata; metadataString: string; + updateMetadataString: () => void; } -const Preview: React.FC = ( { jsPsychMetadata, metadataString } ) => { +const Preview: React.FC = ( { jsPsychMetadata, updateMetadataString } ) => { const [ metadataObject, setMetadataObject ] = useState<{ [key: string]: any }>(jsPsychMetadata.getUserMetadataFields()); // fields const [ authorsList, setAuthorsList ] = useState(jsPsychMetadata.getAuthorList()); // authors const [ variablesList, setVariablesList ] = useState(jsPsychMetadata.getVariableList()); // variables + const [isPopupOpen, setIsPopupOpen] = useState(false); + const [popupType, setPopupType] = useState(''); + const [popupData, setPopupData] = useState({}); // State to hold popup-specific data + + const openPopup = (type: string, data?: any) => { + setPopupType(type); + setIsPopupOpen(true); + if (data) setPopupData(data); else {};// Set optional data to pass to child popups + }; + + const closePopup = () => { + setIsPopupOpen(false); + setPopupType(''); + setPopupData({}); + }; + + const filterEmptyFields = (obj: Record): Record => { + return Object.entries(obj).reduce((acc, [key, value]) => { + if (value !== undefined && value !== "" && (Array.isArray(value) ? value.length > 0 : true)) { + acc[key] = value; + } + return acc; + }, {} as Record); + } + + // THE AUTHOR single reference is not working as intended -> data not loaded correclty + // if there is an oldName, willl want to delete it and rewrite new + const handleSave = (formData: FieldFormData | AuthorFormData | VariableFormData, type: string, oldName?: string) => { + switch (type){ + case 'field': + const fieldData = formData as FieldFormData; // typecasting + + if ("fieldName" in fieldData && "fieldDescription" in fieldData && oldName && oldName !== ""){ + jsPsychMetadata.deleteMetadataField(oldName); + } + + jsPsychMetadata.setMetadataField(fieldData["fieldName"], fieldData["fieldDescription"]); + break; + case 'author': + const author = formData as AuthorFormData; // typecasting + const filteredAuthor = filterEmptyFields(author) as AuthorFields; + + // neeed to delete old reference + if ("name" in filteredAuthor && oldName && oldName !== "")jsPsychMetadata.deleteAuthor(oldName); + + jsPsychMetadata.setAuthor(filteredAuthor); // else case error? -> this else shouldn't be reachable so not sure how to handle + break; + case 'variable': + const variable = formData as VariableFormData; + const filteredVariable = filterEmptyFields(variable) as VariableFields; + + if ("name" in filteredVariable && oldName && oldName !== "") jsPsychMetadata.deleteVariable(oldName); + + jsPsychMetadata.setVariable(filteredVariable); // else case error? -> this else shouldn't be reachable so not sure how to handle + break; + default: + console.warn("Submitting form returning with undefined type:", type); + return; + } + + // updateMetadataString(); // calls update to the UI string and is broken + } + + const renderPopup = () => { + switch (popupType) { + case 'list': + return ; + case 'field': + return ; + case 'author': + return ; + case 'variables': + return ; + default: + return null; + } + }; + const updateState = () => { setMetadataObject(jsPsychMetadata.getUserMetadataFields()); setAuthorsList(jsPsychMetadata.getAuthorList()); @@ -65,6 +149,8 @@ const Preview: React.FC = ( { jsPsychMetadata, metadataString } )
           {variables()}
         
+ + {isPopupOpen && renderPopup()} ) diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index bc70a0e..5412161 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -75,6 +75,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin updateMetadataString(); // calls update to the UI string and is broken } + // can clean up setPopupType and setPopupData const renderPopup = () => { switch (popupType) { case 'list': @@ -98,7 +99,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin - + {isPopupOpen && renderPopup()} {/* */} diff --git a/packages/frontend/src/pages/ViewOptions.tsx b/packages/frontend/src/pages/ViewOptions.tsx index bc49d15..8da8332 100644 --- a/packages/frontend/src/pages/ViewOptions.tsx +++ b/packages/frontend/src/pages/ViewOptions.tsx @@ -15,7 +15,7 @@ const ViewOptions: React.FC = ( {jsPsychMetadata, metadataStri <>
- +
From 1851c88a4d0a4381a2db2426af57f91f685a6d1c Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 14:35:28 -0700 Subject: [PATCH 10/23] Fixed functionality for buttons and editing and states for the preview screen --- .../frontend/src/components/ListItems.tsx | 90 ++++++++----------- packages/frontend/src/components/Preview.tsx | 56 ++++-------- packages/frontend/src/pages/Options.tsx | 1 + 3 files changed, 56 insertions(+), 91 deletions(-) diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index 477f49d..55b7cf3 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -1,38 +1,31 @@ import JsPsychMetadata from '@jspsych/metadata'; -import React, { useState } from 'react'; -import Trash from '../assets/trash.svg' +import React from 'react'; +import Trash from '../assets/trash.svg'; type ListItemsProps = { jsPsychMetadata: JsPsychMetadata; - setPopupType: (type: string) => void; // Update setPopupType to accept optional data - setPopupData: (data: any) => void; updateMetadataString: () => void; openPopup: (type: string, data?: any) => void; - closePopup: () => void; // shoudl be passed to the other item -} + data: Record; +} type Author = { - /** The type of the author. */ "@type"?: string; - /** The name of the author. (required) */ name: string; - /** The given name of the author. */ givenName?: string; - /** The family name of the author. */ familyName?: string; - /** The identifier that distinguishes the author across datasets (URL). */ identifier?: string; } type VariableMeasured = { "@type": string; - name: string; // required + name: string; description: string | Record; value: string | boolean | number; - identifier?: string; // identifier that distinguishes across dataset (URL) + identifier?: string; minValue?: number; maxValue?: number; - levels?: string[]; // array of string values + levels?: string[]; levelsOrdered?: boolean; na?: boolean; naValue?: string; @@ -51,52 +44,47 @@ type Metadata = { [key: string]: any; } -const ListItems: React.FC = ({ jsPsychMetadata, setPopupType, setPopupData, updateMetadataString, openPopup, closePopup }) => { +const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataString, openPopup, data }) => { const generateButtons = (metadata: Metadata) => { const res = []; for (const key in metadata) { const value = metadata[key]; - if (key === "variableMeasured"){ - for (const variable_key in value){ + if (key === "variableMeasured") { + for (const variable_key in value) { const variable = value[variable_key]; res.push(
-
); } - } else if (key === "author"){ - for (const author_key in value){ + } else if (key === "author") { + for (const author_key in value) { const author = value[author_key]; - let author_typing: {}; - if (typeof author === "string") author_typing = { "name": author }; // fix name only issues - + const author_typing = typeof author === "string" ? { name: author } : author; + res.push(
-
); @@ -104,27 +92,23 @@ const ListItems: React.FC = ({ jsPsychMetadata, setPopupType, se } else { res.push(
- + onClick={() => handleDelete(key, 'field')} + className="delete-button" + > + Trash +
); } } return res; - } - - const [buttons, setbuttons] = useState(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); + } const handleDelete = (name: string, type: string) => { switch (type) { @@ -133,28 +117,26 @@ const ListItems: React.FC = ({ jsPsychMetadata, setPopupType, se break; case 'author': jsPsychMetadata.deleteAuthor(name); - break + break; case 'field': jsPsychMetadata.deleteMetadataField(name); - break + break; + default: + console.warn('Unhandled type for deletion:', type); } - // updates the UI -> need update String - setbuttons(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); updateMetadataString(); } - return (
-

This is the listeItems page

- {buttons} + {generateButtons(data as Metadata)}
- ) + ); } -export default ListItems; +export default ListItems; \ No newline at end of file diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index c738a09..66a2514 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -13,9 +13,9 @@ interface PreviewProps { } const Preview: React.FC = ( { jsPsychMetadata, updateMetadataString } ) => { - const [ metadataObject, setMetadataObject ] = useState<{ [key: string]: any }>(jsPsychMetadata.getUserMetadataFields()); // fields - const [ authorsList, setAuthorsList ] = useState(jsPsychMetadata.getAuthorList()); // authors - const [ variablesList, setVariablesList ] = useState(jsPsychMetadata.getVariableList()); // variables + const [ metadataFields, setMetadataFields ] = useState<{ [key: string]: any }>(jsPsychMetadata.getUserMetadataFields()); // fields + const [ authorsList, setAuthorsList ] = useState({author: jsPsychMetadata.getAuthorList()}); // authors + const [ variablesList, setVariablesList ] = useState({ variableMeasured: jsPsychMetadata.getVariableList()}); // variables const [isPopupOpen, setIsPopupOpen] = useState(false); const [popupType, setPopupType] = useState(''); @@ -77,7 +77,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin return; } - // updateMetadataString(); // calls update to the UI string and is broken + updateState(); } const renderPopup = () => { @@ -96,41 +96,24 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin }; const updateState = () => { - setMetadataObject(jsPsychMetadata.getUserMetadataFields()); - setAuthorsList(jsPsychMetadata.getAuthorList()); - setVariablesList(jsPsychMetadata.getVariableList()); - } - - const fields = () => { - var res = ""; + const newMetadataFields = jsPsychMetadata.getUserMetadataFields(); + const newAuthorsList = { author: jsPsychMetadata.getAuthorList() }; + const newVariablesList = { variableMeasured: jsPsychMetadata.getVariableList() }; - for (const key in metadataObject){ - res += key + ": " + metadataObject[key] + "\n"; + if (JSON.stringify(metadataFields) !== JSON.stringify(newMetadataFields)) { + console.log("stringify metadatadatafields"); + setMetadataFields(newMetadataFields); } + if (JSON.stringify(authorsList) !== JSON.stringify(newAuthorsList)) { + console.log("stringify author"); - return res; - } - - const authors = () => { - var res = ""; - - for (const key in authorsList){ - res += authorsList[key] + "\n"; + setAuthorsList(newAuthorsList); } + if (JSON.stringify(variablesList) !== JSON.stringify(newVariablesList)) { + console.log("stringify var"); - return res; - } - - // need to build out logic for how to increment thorugh these - const variables = () => { - var res = ""; - - // likely go through the fields and pick some that we want, and others that don't want - for (const key in variablesList){ - res += JSON.stringify(variablesList[key]) + "\n"; + setVariablesList(newVariablesList); } - - return res; } return ( @@ -139,17 +122,16 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Fields

-          {fields()}
+          
         

Authors

-          {authors()}
+          
         

Variables

-          {variables()}
+          
         
- {isPopupOpen && renderPopup()}
diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 5412161..377ff8e 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -100,6 +100,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin + {isPopupOpen && renderPopup()} {/* */} From ff3e0995637e8f0e88ccdda77b25add87e1dcda2 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 15:56:41 -0700 Subject: [PATCH 11/23] Functionality working, working to move add buttons --- packages/frontend/src/assets/plus.svg | 14 ++++++++++++++ packages/frontend/src/components/Preview.tsx | 8 +++++++- packages/frontend/src/pages/Options.tsx | 4 +--- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 packages/frontend/src/assets/plus.svg diff --git a/packages/frontend/src/assets/plus.svg b/packages/frontend/src/assets/plus.svg new file mode 100644 index 0000000..d594686 --- /dev/null +++ b/packages/frontend/src/assets/plus.svg @@ -0,0 +1,14 @@ + + + + plus-circle + Created with Sketch Beta. + + + + + + + + + \ No newline at end of file diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 66a2514..75a99ec 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -5,6 +5,7 @@ import AuthorPopup, { AuthorFormData } from './popups/AuthorPopup'; import VariablePopup, { VariableFormData } from './popups/VariablePopup'; import ListPopup from '../components/popups/ListPopup'; import ListItems from './ListItems'; +import Plus from '../assets/plus.svg'; interface PreviewProps { jsPsychMetadata: JsPsychMetadata; @@ -120,7 +121,12 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Metadata preview

-

Fields

+
+

Fields

+ +
           
         
diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 377ff8e..3f81f3f 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -96,14 +96,12 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Metadata Options

It is highly advised you enter author information and edit the title

- + {/* might not need */} - {isPopupOpen && renderPopup()} - {/* */}
); From 0d46f8ad9e192d9f8d1f817a425a58fd189c3bb8 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 16:07:24 -0700 Subject: [PATCH 12/23] finalizing metadata preview screen --- packages/frontend/src/App.css | 18 ++++++++++++++++++ packages/frontend/src/components/Preview.tsx | 17 +++++++++++++---- packages/frontend/src/pages/Options.tsx | 2 +- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index ebfdfad..d299c60 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -181,3 +181,21 @@ html, body { margin-bottom: 32px; } + +/* Preview page */ + +.preview-header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +.previewButton { + margin: 10px; + padding: 0px; + width: 20px; + height: 20px; + border-radius: 5px; + background: none; +} \ No newline at end of file diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index 75a99ec..e09aa65 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -123,19 +123,28 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Fields

-
           
         
-

Authors

+
+

Authors

+ +
           
         
-

Variables

-
+        
+

Variables

+ +
           
         
{isPopupOpen && renderPopup()} diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 3f81f3f..173a880 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -97,7 +97,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Metadata Options

It is highly advised you enter author information and edit the title

{/* might not need */} - + {/* */} From ac071259b5228846565a4d627b33fd6ae3729d02 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Wed, 7 Aug 2024 18:31:21 -0700 Subject: [PATCH 13/23] Fixing state updates with delete buttons --- packages/frontend/src/components/ListItems.tsx | 5 ++++- packages/frontend/src/components/Preview.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index 55b7cf3..93aade6 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -7,6 +7,7 @@ type ListItemsProps = { updateMetadataString: () => void; openPopup: (type: string, data?: any) => void; data: Record; + updateState: () => void; } type Author = { @@ -44,7 +45,7 @@ type Metadata = { [key: string]: any; } -const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataString, openPopup, data }) => { +const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataString, openPopup, data, updateState }) => { const generateButtons = (metadata: Metadata) => { const res = []; for (const key in metadata) { @@ -125,6 +126,8 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt console.warn('Unhandled type for deletion:', type); } + + updateState(); updateMetadataString(); } diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index e09aa65..ea04271 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -22,6 +22,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin const [popupType, setPopupType] = useState(''); const [popupData, setPopupData] = useState({}); // State to hold popup-specific data + // can use this to replace setPopupType and setPopupData const openPopup = (type: string, data?: any) => { setPopupType(type); setIsPopupOpen(true); @@ -112,7 +113,6 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin } if (JSON.stringify(variablesList) !== JSON.stringify(newVariablesList)) { console.log("stringify var"); - setVariablesList(newVariablesList); } } @@ -128,7 +128,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin
-          
+          
         

Authors

@@ -137,7 +137,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin
-          
+          
         

Variables

@@ -145,7 +145,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin Trash
-          
+          
         
{isPopupOpen && renderPopup()}
From cc82e77ec04dd27cd27dfdee9dd1aca046e16d74 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Thu, 8 Aug 2024 14:29:23 -0700 Subject: [PATCH 14/23] Setup screen to allow for default fields --- packages/frontend/src/App.css | 4 +- .../frontend/src/components/ListItems.tsx | 2 + .../src/components/popups/ListPopup.tsx | 100 +++--------------- packages/frontend/src/pages/Options.tsx | 9 +- 4 files changed, 26 insertions(+), 89 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index d299c60..8eef1ff 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -194,8 +194,8 @@ html, body { .previewButton { margin: 10px; padding: 0px; - width: 20px; - height: 20px; + width: 22px; + height: 22px; border-radius: 5px; background: none; } \ No newline at end of file diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index 93aade6..199f537 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -2,6 +2,8 @@ import JsPsychMetadata from '@jspsych/metadata'; import React from 'react'; import Trash from '../assets/trash.svg'; +// hover and adding more automatic options + type ListItemsProps = { jsPsychMetadata: JsPsychMetadata; updateMetadataString: () => void; diff --git a/packages/frontend/src/components/popups/ListPopup.tsx b/packages/frontend/src/components/popups/ListPopup.tsx index 7c5a776..669ef18 100644 --- a/packages/frontend/src/components/popups/ListPopup.tsx +++ b/packages/frontend/src/components/popups/ListPopup.tsx @@ -8,6 +8,7 @@ type ListPopup = { setPopupType: (type: string) => void; // Update setPopupType to accept optional data setPopupData: (data: any) => void; updateMetadataString: () => void; + data?: Record; } type Author = { @@ -50,100 +51,29 @@ type Metadata = { [key: string]: any; } -const ListPopup: React.FC = ({ jsPsychMetadata, onClose, setPopupType, setPopupData, updateMetadataString }) => { +const ListPopup: React.FC = ({ jsPsychMetadata, onClose, setPopupType, setPopupData, updateMetadataString, data }) => { + const generateButtons = (metadata: Metadata) => { const res = []; for (const key in metadata) { const value = metadata[key]; - if (key === "variableMeasured"){ - for (const variable_key in value){ - const variable = value[variable_key]; - - res.push( -
- - -
- ); - } - } else if (key === "author"){ - for (const author_key in value){ - const author = value[author_key]; - - res.push( -
- - -
- ); - } - } else { - res.push( -
- - -
- ); - } + res.push( +
+ +
+ ); } return res; } - const [buttons, setbuttons] = useState(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); - - const handleDelete = (name: string, type: string) => { - switch (type) { - case 'variable': - jsPsychMetadata.deleteVariable(name); - break; - case 'author': - jsPsychMetadata.deleteAuthor(name); - break - case 'field': - jsPsychMetadata.deleteMetadataField(name); - break - } - - // updates the UI -> need update String - setbuttons(generateButtons(jsPsychMetadata.getMetadata() as Metadata)); - updateMetadataString(); - } - return (
@@ -151,7 +81,7 @@ const ListPopup: React.FC = ({ jsPsychMetadata, onClose, setPopupType

This is the listPopup page

- {buttons} + {data ? generateButtons(data as Metadata) : generateButtons(jsPsychMetadata.getMetadata() as Metadata)}
diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index 173a880..d3db46f 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -75,11 +75,16 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin updateMetadataString(); // calls update to the UI string and is broken } + const default_vars = { + "default": "one way to use this is blabhlah" + } + + // can clean up setPopupType and setPopupData const renderPopup = () => { switch (popupType) { case 'list': - return ; + return ; case 'field': return ; case 'author': @@ -96,7 +101,7 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin

Metadata Options

It is highly advised you enter author information and edit the title

- {/* might not need */} + {/* */} From 5a72089bc4afc59b905b0ef616c745fcaa916f9a Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Thu, 8 Aug 2024 15:08:51 -0700 Subject: [PATCH 15/23] Building out browse fields feature --- packages/frontend/src/components/Preview.tsx | 2 +- .../src/components/popups/ListPopup.tsx | 56 ++++++++++++++----- packages/frontend/src/pages/Options.tsx | 19 ++++++- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/packages/frontend/src/components/Preview.tsx b/packages/frontend/src/components/Preview.tsx index ea04271..757ce25 100644 --- a/packages/frontend/src/components/Preview.tsx +++ b/packages/frontend/src/components/Preview.tsx @@ -85,7 +85,7 @@ const Preview: React.FC = ( { jsPsychMetadata, updateMetadataStrin const renderPopup = () => { switch (popupType) { case 'list': - return ; + return ; case 'field': return ; case 'author': diff --git a/packages/frontend/src/components/popups/ListPopup.tsx b/packages/frontend/src/components/popups/ListPopup.tsx index 669ef18..4a42dbf 100644 --- a/packages/frontend/src/components/popups/ListPopup.tsx +++ b/packages/frontend/src/components/popups/ListPopup.tsx @@ -1,6 +1,5 @@ import JsPsychMetadata from '@jspsych/metadata'; -import React, { useState } from 'react'; -import Trash from '../../assets/trash.svg' +import React from 'react'; type ListPopup = { jsPsychMetadata: JsPsychMetadata; @@ -8,6 +7,7 @@ type ListPopup = { setPopupType: (type: string) => void; // Update setPopupType to accept optional data setPopupData: (data: any) => void; updateMetadataString: () => void; + openPopup: (type: string, data?: any) => void; data?: Record; } @@ -51,24 +51,50 @@ type Metadata = { [key: string]: any; } -const ListPopup: React.FC = ({ jsPsychMetadata, onClose, setPopupType, setPopupData, updateMetadataString, data }) => { +const ListPopup: React.FC = ({ jsPsychMetadata, onClose, data, openPopup }) => { const generateButtons = (metadata: Metadata) => { const res = []; for (const key in metadata) { const value = metadata[key]; - res.push( -
- -
- ); + if (key === "variableMeasured") { + for (const variable_key in value) { + const variable = value[variable_key]; + + res.push( +
+ +
+ ); + } + } else if (key === "author") { + for (const author_key in value) { + const author = value[author_key]; + const author_typing = typeof author === "string" ? { name: author } : author; + + res.push( +
+ +
+ ); + } + } else { + res.push( +
+ +
+ ); + } } return res; @@ -78,7 +104,7 @@ const ListPopup: React.FC = ({ jsPsychMetadata, onClose, setPopupType return (
-

This is the listPopup page

+

These are fields commonly added to Psych-DS metadata to describe datasets. Clicking on them will allow you to fill in variable information.

{data ? generateButtons(data as Metadata) : generateButtons(jsPsychMetadata.getMetadata() as Metadata)} diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index d3db46f..f9700ee 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -76,15 +76,28 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin } const default_vars = { - "default": "one way to use this is blabhlah" + "citation": "Acknowledging original authors (URL or scholarly work)", + "license": "Author-assigned 'license' for data/material use (URL preferred)", + "funder": "List of sources of funding (grant numbers, person or organization)", + "url": "The canonical source for the dataset.", + "identifier": "Identifier(s) that uniquely distinguish the dataset (e.g., DOI, PMID, etc.).", + "privacyPolicy": "One of open, private, open_deidentified, or open_redacted.", + keywords: "Comma-separated keywords used to assist search.", + "author": { + additional_author: "Name of additional authors not defined" + }, + "variableMeasured": { + additional_variable: { + name: "Name of variable created during not added when generating defaults" + } + } } - // can clean up setPopupType and setPopupData const renderPopup = () => { switch (popupType) { case 'list': - return ; + return ; case 'field': return ; case 'author': From 65c80eef32993fdd5abafbc449b01d9b7d26e91f Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Fri, 9 Aug 2024 16:26:33 -0700 Subject: [PATCH 16/23] Updating prompting to include back buttons and cleaning up UI of authorsPrompt --- packages/frontend/src/App.css | 8 +++++++ .../src/components/upload/AuthorForm.tsx | 20 ++++++++++++----- .../src/components/upload/PromptForm.tsx | 10 ++++++++- packages/frontend/src/pages/Upload.tsx | 22 ++++++++++++++----- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index 8eef1ff..90e9a8c 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -198,4 +198,12 @@ html, body { height: 22px; border-radius: 5px; background: none; +} + +/* Button Container */ + +.backSubmitButtonContainer { + display: flex; + flex-direction: row; + gap: 15px; } \ No newline at end of file diff --git a/packages/frontend/src/components/upload/AuthorForm.tsx b/packages/frontend/src/components/upload/AuthorForm.tsx index de66cdf..40141e3 100644 --- a/packages/frontend/src/components/upload/AuthorForm.tsx +++ b/packages/frontend/src/components/upload/AuthorForm.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { AuthorFields } from '../../../../metadata/dist/AuthorsMap'; import JsPsychMetadata from '@jspsych/metadata'; +import Plus from '../../assets/plus.svg'; interface AuthorFormProps { jsPsychMetadata: JsPsychMetadata; @@ -73,13 +74,23 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata } } - handleScreenChange('data', 'skip'); + handleScreenChange('data', 'Skip'); updateMetadataString(); } + const handleBack = (e: React.FormEvent) => { + e.preventDefault(); + handleScreenChange('form', 'Save'); + } + return (
-

Authors

+
+

Authors

+ +

The name field is required and any author missing the name field will not be added. This can be edited and authors can be added later.

{authors.map((author, index) => (
@@ -103,10 +114,7 @@ const AuthorForm: React.FC = ({ jsPsychMetadata, updateMetadata
))} - +
); diff --git a/packages/frontend/src/components/upload/PromptForm.tsx b/packages/frontend/src/components/upload/PromptForm.tsx index 24f5547..4157328 100644 --- a/packages/frontend/src/components/upload/PromptForm.tsx +++ b/packages/frontend/src/components/upload/PromptForm.tsx @@ -36,6 +36,11 @@ const PromptForm: React.FC = ({ jsPsychMetadata, updateMetadata handleScreenChange('author', 'Save'); }; + const handleBack = (e: React.FormEvent) => { + e.preventDefault(); + handleScreenChange('metadata', 'Skip'); + } + return ( <>

Project Information

@@ -60,7 +65,10 @@ const PromptForm: React.FC = ({ jsPsychMetadata, updateMetadata onChange={handleChange} />
- +
+ + +
); diff --git a/packages/frontend/src/pages/Upload.tsx b/packages/frontend/src/pages/Upload.tsx index 29f1e3d..3482840 100644 --- a/packages/frontend/src/pages/Upload.tsx +++ b/packages/frontend/src/pages/Upload.tsx @@ -23,15 +23,19 @@ const Upload: React.FC = ( { jsPsychMetadata, setPage, updateMetada switch (newPage) { case 'metadata': setPageNumber(1); + setButtonText("Skip"); break; case 'form': setPageNumber(2); + // setButtonText("Save"); - handled within components & static break; case 'author': setPageNumber(3); + // setButtonText("Submit"); - handled within components & static break; case 'data': setPageNumber(4); + setButtonText("Skip"); } } @@ -57,8 +61,7 @@ const Upload: React.FC = ( { jsPsychMetadata, setPage, updateMetada return ; @@ -67,14 +70,23 @@ const Upload: React.FC = ( { jsPsychMetadata, setPage, updateMetada case 'author': return; // this will be handled internally to tie behavior with form case 'data': - return + ; + +
; } } From 4cca56004bf8c720fb4f3ff8c52844e1f19dfcee Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Fri, 9 Aug 2024 16:44:13 -0700 Subject: [PATCH 17/23] Intial version of the hover working --- packages/frontend/src/App.css | 6 +++ .../frontend/src/components/ListItems.tsx | 45 ++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index 90e9a8c..f2337f9 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -206,4 +206,10 @@ html, body { display: flex; flex-direction: row; gap: 15px; +} + +/* HoverPopup */ +.hover-popup { + white-space: normal; /* Allows text to wrap */ + word-wrap: break-word; /* Ensures long words or URLs wrap to the next line */ } \ No newline at end of file diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index 199f537..2fa0a0b 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -1,5 +1,5 @@ import JsPsychMetadata from '@jspsych/metadata'; -import React from 'react'; +import React, { useState} from 'react'; import Trash from '../assets/trash.svg'; // hover and adding more automatic options @@ -46,8 +46,9 @@ type Metadata = { variableMeasured: VariableMeasured[]; [key: string]: any; } - const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataString, openPopup, data, updateState }) => { + const [hoveredItem, setHoveredItem] = useState(null); + const generateButtons = (metadata: Metadata) => { const res = []; for (const key in metadata) { @@ -58,11 +59,22 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt const variable = value[variable_key]; res.push( -
+
setHoveredItem("variable" + variable_key)} + onMouseLeave={() => setHoveredItem(null)} + > + {hoveredItem === "variable" + variable_key && ( +
+

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

+ {/* Add more information as needed */} +
+ )} + {hoveredItem === "author" + author_key && ( +
+

{author_typing.givenName} {author_typing.familyName}

+ {/* Add more information as needed */} +
+ )} + {hoveredItem === "field" + key && ( +
+

{value}

+ {/* Add more information as needed */} +
+ )} {hoveredItem === "author" + author_key && (
-

{author_typing.givenName} {author_typing.familyName}

- {/* Add more information as needed */} + {Object.entries(author_typing).map(([key, value]) => { + // Skip the description field and empty fields + if (value === '' || value === null || value === undefined || key === "name") return null; + + // Convert non-primitive types to string using JSON.stringify + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); // Ensures the value is a string or a valid ReactNode + + return ( +

+ {key}: {displayValue} +

+ ); + })}
)} - {hoveredItem === "variable" + variable_key && ( -
-

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

- {Object.entries(variable).map(([key, value]) => { - // Skip the description field and empty fields - if (key === 'description' || value === '' || value === null || value === undefined || key === "@type" || key === "name") return null; - - // Convert non-primitive types to string using JSON.stringify - const displayValue = (typeof value === 'object' || typeof value === 'function') - ? JSON.stringify(value, null, 2) - : String(value); // Ensures the value is a string or a valid ReactNode - - return ( -

- {key}: {displayValue} -

- ); - })} -
- )} +
+ + {hoveredItem === "variable" + variable_key && ( +
+

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

+ {Object.entries(variable).map(([key, value]) => { + // Skip the description field and empty fields + if (key === 'description' || value === '' || value === null || value === undefined || key === "@type" || key === "name") return null; + + // Convert non-primitive types to string using JSON.stringify + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); // Ensures the value is a string or a valid ReactNode + + return ( +

+ {key}: {displayValue} +

+ ); + })} +
+ )} +
- {hoveredItem === "author" + author_key && ( -
- {Object.entries(author_typing).map(([key, value]) => { - // Skip the description field and empty fields - if (value === '' || value === null || value === undefined || key === "name") return null; - - // Convert non-primitive types to string using JSON.stringify - const displayValue = (typeof value === 'object' || typeof value === 'function') - ? JSON.stringify(value, null, 2) - : String(value); // Ensures the value is a string or a valid ReactNode - - return ( -

- {key}: {displayValue} -

- ); - })} -
- )} +
+ + {hoveredItem === "author" + author_key && ( +
+ {Object.entries(author_typing).map(([key, value]) => { + // Skip the description field and empty fields + if (value === '' || value === null || value === undefined || key === "name") return null; + + // Convert non-primitive types to string using JSON.stringify + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); // Ensures the value is a string or a valid ReactNode + + return ( +

+ {key}: {displayValue} +

+ ); + })} +
+ )} +
- {hoveredItem === "field" + key && ( -
-

{value}

- {/* Add more information as needed */} -
- )} +
+ + {hoveredItem === "field" + key && ( +
+

{value}

+ {/* Add more information as needed */} +
+ )} +
+
); } diff --git a/packages/frontend/src/pages/Options.tsx b/packages/frontend/src/pages/Options.tsx index f9700ee..f5180d0 100644 --- a/packages/frontend/src/pages/Options.tsx +++ b/packages/frontend/src/pages/Options.tsx @@ -113,7 +113,9 @@ const Options: React.FC = ( { jsPsychMetadata, updateMetadataStrin <>

Metadata Options

-

It is highly advised you enter author information and edit the title

+

You can browse fields that are commonly added to Psych-DS datasets with their corresponding descriptions, return the to the previous screen to upload additional data, + or download the metadata as a dataset_description.json file. +

{/* */} From d6b001d8d8aca6f1b230d2b4fc13b45233d07c02 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 12 Aug 2024 14:14:00 -0700 Subject: [PATCH 20/23] Updating styling to use expanding vs hovering --- packages/frontend/src/App.css | 39 ++++- packages/frontend/src/assets/downarrow.svg | 14 ++ packages/frontend/src/assets/uparrow.svg | 14 ++ .../frontend/src/components/ListItems.tsx | 137 ++++++++---------- 4 files changed, 130 insertions(+), 74 deletions(-) create mode 100644 packages/frontend/src/assets/downarrow.svg create mode 100644 packages/frontend/src/assets/uparrow.svg diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index 7097ff3..084db31 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -216,4 +216,41 @@ html, body { .hover-popup-title-container { padding: 5px; -} \ No newline at end of file + display: flex; + flex-direction: row; +} + +.hover-popup-title-container button:first-of-type { + margin-right: 4px; /* Adjust the margin to make the gap smaller */ +} + +.hover-popup-title-container button:last-of-type { + margin-left: 4px; /* Optional: Adjust if needed for symmetry */ +} + +.delete-button-hover { + /* display: inline-flex; */ + padding: 5px 5px; + align-items: center; /* Centers the content vertically within the button */ + justify-content: center; /* Centers the content horizontally within the button */ +} + + +.delete-button-hover img { + width: 20px; /* Larger image size */ + height: 20px; /* Larger image size */ +} + +.variable-item-hover-popup, .field-item-hover-popup, .author-item-hover-popup { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid #ddd; /* Optional: add border for better separation */ + border-radius: 10px; /* Optional: add rounded corners */ + gap: 5px; + padding-left: 8px; + padding-right: 8px; + margin-bottom: 10px; +} + diff --git a/packages/frontend/src/assets/downarrow.svg b/packages/frontend/src/assets/downarrow.svg new file mode 100644 index 0000000..b2e3bb2 --- /dev/null +++ b/packages/frontend/src/assets/downarrow.svg @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/packages/frontend/src/assets/uparrow.svg b/packages/frontend/src/assets/uparrow.svg new file mode 100644 index 0000000..7f7517c --- /dev/null +++ b/packages/frontend/src/assets/uparrow.svg @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index b3f5039..dd53c1d 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -1,8 +1,8 @@ import JsPsychMetadata from '@jspsych/metadata'; -import React, { useState} from 'react'; +import React, { useState } from 'react'; import Trash from '../assets/trash.svg'; - -// hover and adding more automatic options +import UpArrow from '../assets/uparrow.svg'; +import DownArrow from '../assets/downarrow.svg'; type ListItemsProps = { jsPsychMetadata: JsPsychMetadata; @@ -46,8 +46,17 @@ type Metadata = { variableMeasured: VariableMeasured[]; [key: string]: any; } + const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataString, openPopup, data, updateState }) => { - const [hoveredItem, setHoveredItem] = useState(null); + const [expandedItems, setExpandedItems] = useState([]); + + const toggleExpand = (itemKey: string) => { + setExpandedItems((prevExpandedItems) => + prevExpandedItems.includes(itemKey) + ? prevExpandedItems.filter((key) => key !== itemKey) + : [...prevExpandedItems, itemKey] + ); + }; const generateButtons = (metadata: Metadata) => { const res = []; @@ -57,30 +66,31 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt if (key === "variableMeasured") { for (const variable_key in value) { const variable = value[variable_key]; + const isExpanded = expandedItems.includes("variable" + variable_key); res.push( -
setHoveredItem("variable" + variable_key)} - onMouseLeave={() => setHoveredItem(null)} - > +
- + + - {hoveredItem === "variable" + variable_key && ( +
+ + {isExpanded && (

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

{Object.entries(variable).map(([key, value]) => { - // Skip the description field and empty fields if (key === 'description' || value === '' || value === null || value === undefined || key === "@type" || key === "name") return null; - // Convert non-primitive types to string using JSON.stringify - const displayValue = (typeof value === 'object' || typeof value === 'function') - ? JSON.stringify(value, null, 2) - : String(value); // Ensures the value is a string or a valid ReactNode + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); return (

@@ -90,13 +100,6 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt })}

)} -
-
); } @@ -104,29 +107,29 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt for (const author_key in value) { const author = value[author_key]; const author_typing = typeof author === "string" ? { name: author } : author; + const isExpanded = expandedItems.includes("author" + author_key); res.push( -
setHoveredItem("author" + author_key)} - onMouseLeave={() => setHoveredItem(null)} - > +
- + - {hoveredItem === "author" + author_key && ( + +
+ {isExpanded && (
{Object.entries(author_typing).map(([key, value]) => { - // Skip the description field and empty fields if (value === '' || value === null || value === undefined || key === "name") return null; - // Convert non-primitive types to string using JSON.stringify - const displayValue = (typeof value === 'object' || typeof value === 'function') - ? JSON.stringify(value, null, 2) - : String(value); // Ensures the value is a string or a valid ReactNode + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); return (

@@ -136,50 +139,38 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt })}

)} -
-
); } } else { + const isExpanded = expandedItems.includes("field" + key); + res.push( -
setHoveredItem("field" + key)} - onMouseLeave={() => setHoveredItem(null)} - > +
- + + - {hoveredItem === "field" + key && ( -
-

{value}

- {/* Add more information as needed */} -
- )}
- - + {isExpanded && ( +
+

{value}

+ {/* Add more information as needed */} +
+ )}
); } } return res; - } + }; const handleDelete = (name: string, type: string) => { switch (type) { @@ -198,7 +189,7 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt updateState(); updateMetadataString(); - } + }; return (
@@ -209,6 +200,6 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt
); -} +}; export default ListItems; \ No newline at end of file From fe05f1b430512764f6d239aa9e7855d9145f51b6 Mon Sep 17 00:00:00 2001 From: vzhang03 Date: Mon, 12 Aug 2024 14:24:52 -0700 Subject: [PATCH 21/23] Finalizing styling for popup --- packages/frontend/src/App.css | 11 ++- .../frontend/src/components/ListItems.tsx | 67 ++++++++++++------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/packages/frontend/src/App.css b/packages/frontend/src/App.css index 084db31..3c42f76 100644 --- a/packages/frontend/src/App.css +++ b/packages/frontend/src/App.css @@ -241,16 +241,21 @@ html, body { height: 20px; /* Larger image size */ } +.expand-button { + padding-left: 12px; + padding-right: 12px; +} + .variable-item-hover-popup, .field-item-hover-popup, .author-item-hover-popup { display: flex; flex-direction: column; align-items: center; justify-content: center; - border: 1px solid #ddd; /* Optional: add border for better separation */ + border: 1px solid #e0e0e0; /* Even lighter gray border */ border-radius: 10px; /* Optional: add rounded corners */ gap: 5px; - padding-left: 8px; - padding-right: 8px; + /* padding-left: 8px; + padding-right: 8px; */ margin-bottom: 10px; } diff --git a/packages/frontend/src/components/ListItems.tsx b/packages/frontend/src/components/ListItems.tsx index dd53c1d..29b50fa 100644 --- a/packages/frontend/src/components/ListItems.tsx +++ b/packages/frontend/src/components/ListItems.tsx @@ -69,10 +69,17 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt const isExpanded = expandedItems.includes("variable" + variable_key); res.push( -
+
-
{isExpanded && ( -
-

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

- {Object.entries(variable).map(([key, value]) => { - if (key === 'description' || value === '' || value === null || value === undefined || key === "@type" || key === "name") return null; - - const displayValue = (typeof value === 'object' || typeof value === 'function') - ? JSON.stringify(value, null, 2) - : String(value); - - return ( -

- {key}: {displayValue} -

- ); - })} -
- )} +
+

{typeof variable.description === 'object' ? JSON.stringify(variable.description, null, 2) : variable.description}

+ {Object.entries(variable).map(([key, value]) => { + if (key === 'description' || value === '' || value === null || value === undefined || key === "@type" || key === "name") return null; + + const displayValue = (typeof value === 'object' || typeof value === 'function') + ? JSON.stringify(value, null, 2) + : String(value); + + return ( +

+ {key}: {displayValue} +

+ ); + })} +
+ )}
); } @@ -110,10 +117,14 @@ const ListItems: React.FC = ({ jsPsychMetadata, updateMetadataSt const isExpanded = expandedItems.includes("author" + author_key); res.push( -
+
-
- + = ({ onClose, onSave, currentPopup, set {nameError &&
{nameError}
}
- + = ({ onClose, onSave, currentPopup, set />
- + = ({ onClose, onSave, currentPopup, set />
- + = ( { onClose, onSave, currentPopup const [formData, setFormData] = useState({ "@type": popupData["@type"] || "", name: popupData["name"] || "", // required - description: popupData["description"] || "", + description: (typeof popupData["description"] === 'object'? JSON.stringify(popupData["description"], null, 2): popupData["description"]) || "", value: popupData["value"] || "", // string, boolean, or number identifier: popupData["identifier"] || "", // identifier that distinguish across dataset (URL), confusing should check description minValue: popupData["minValue"] || undefined, @@ -91,7 +91,7 @@ const VariablePopup: React.FC = ( { onClose, onSave, currentPopup
- + = ( { onClose, onSave, currentPopup {nameError &&
{nameError}
}
- - description +