Skip to content

Commit

Permalink
Pages Editor: Basic Styling & Simple Workflow Settings Page (#6822)
Browse files Browse the repository at this point in the history
* PagesEditor: rename index.jsx into its own PagesEditor component

* PagesEditor: add stylesheet

* Lab: add exception for PagesEditor styling

* PagesEditor: update README to include notes on other PFE files affected

* PagesEditor: attempt to style workflow header

* PagesEditor: minor changes to workflowId and projectId use

* WorkflowHeader: minor changes to strings and icons

* WorkflowHeader: add placeholder onClick buttons

* PagesEditor: add WorkflowSettingsPage

* PagesEditor: remove Tester

* PagesEditor: add styles for workflow config page

* PagesEditor: establish structure for styling constants

* WorkflowSettingsPage: add first column

* WorkflowSettingsPage: add grid layout

* WorkflowSettingsPage: style hr

* WorkflowSettingsPage: fix font sizes

* PagesEditor: refactor layout styles

* WorkflowSettingsPage: add Subject Retirement Count update functionality

* WorkflowSettingsPage: minor refactors

* WorkflowSettingsPage: Subject Viewer selection now works

* PagesEditor: refactor styling sizes

* WorkflowSettingsPage: add styling for .disabled

* WorkflowSettingsPage: add tests for checkboxes and radio

* WorkflowSettingsPage: add align-item styles

* WorkflowSettingsPage: add accent colour to controls

* WorkflowSettingsPage: minor layout width change to compensate for padding

* PageEditor: restyle header to match design, add new layout stylings
  • Loading branch information
shaunanoordin authored Sep 18, 2023
1 parent 52757cd commit 34f8fab
Show file tree
Hide file tree
Showing 12 changed files with 559 additions and 40 deletions.
44 changes: 44 additions & 0 deletions app/pages/lab-pages-editor/PagesEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Pages Editor
Main component of the Pages Editor feature.
*/

// ESLint: don't import global React, and don't use .defaultProps.
/* eslint-disable no-console */
/* eslint-disable react/react-in-jsx-scope */
/* eslint-disable react/require-default-props */

import { StrictMode } from 'react';
import PropTypes from 'prop-types';

import DataManager from './DataManager.jsx';
import Tester from './Tester.jsx';
import WorkflowHeader from './components/WorkflowHeader.jsx';
import WorkflowSettingsPage from './components/WorkflowSettingsPage.jsx';

function PagesEditor({ params }) {
const { workflowID: workflowId, projectID: projectId } = params;

return (
<StrictMode>
<div className="lab-pages-editor">
<DataManager
key={workflowId || '-'} //
workflowId={workflowId}
>
<WorkflowHeader projectId={projectId} />
<WorkflowSettingsPage />
</DataManager>
</div>
</StrictMode>
);
}

PagesEditor.propTypes = {
params: PropTypes.shape({
projectID: PropTypes.string,
workflowID: PropTypes.string
})
};

export default PagesEditor;
11 changes: 11 additions & 0 deletions app/pages/lab-pages-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@

The "Pages Editor" is a variant of the Workflow Editor used in the Project Builder.

This experiment started development in late August/early September 2023, and runs parallel to the [FEM Lab](../lab-fem)

### Dev Notes: Changes to Other PFE Files

The Pages Editor aims to be standalone, with minimum changes to other parts of the PFE code base. However, some changes are necessary, so please note the changes on the following PFE files:

- `app/router.jsx`: modified to add PagesEditor route (`lab/{projectId}/workflows/editor/{workflowId}`)
- `css/lab-pages-editor.syl`: added style sheet.
- `css/main.styl`: modified to import PagesEditor stylesheet.
- `app/pages/lab/project.jsx`: modified to include "isPartOfPagesEditorExperiment" flag, which adds a special style to the PagesEditor's container.

44 changes: 44 additions & 0 deletions app/pages/lab-pages-editor/components/WorkflowHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* eslint-disable no-console */
/* eslint-disable react/react-in-jsx-scope */
/* eslint-disable react/require-default-props */

import PropTypes from 'prop-types';
import { Link } from 'react-router';

import ReturnIcon from '../icons/ReturnIcon.jsx';
import { useWorkflowContext } from '../context.js';
import strings from '../strings.json';

export default function WorkflowHeader({
projectId = ''
}) {
const { workflow } = useWorkflowContext();
const returnUrl = `/lab/${projectId}/workflows`;

function onClick() {
console.log('TODO');
}

if (!workflow) return null;

return (
<div className="workflow-header flex-row">
<Link to={returnUrl}>
<ReturnIcon />
{strings.PagesEditor.components.WorkflowHeader.return}
</Link>
<div className="flex-row flex-item justify-around">
<button className="unselected" type="button" onClick={onClick}>
{strings.PagesEditor.components.WorkflowHeader.tasks}
</button>
<button className="selected" type="button" onClick={onClick}>
{strings.PagesEditor.components.WorkflowHeader.workflow_settings}
</button>
</div>
</div>
);
}

WorkflowHeader.propTypes = {
projectId: PropTypes.string
};
255 changes: 255 additions & 0 deletions app/pages/lab-pages-editor/components/WorkflowSettingsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/* eslint-disable no-console */
/* eslint-disable react/react-in-jsx-scope */
/* eslint-disable react/require-default-props */
/* eslint-disable radix */

import { useWorkflowContext } from '../context.js';
import strings from '../strings.json'; // TODO: move all text into strings

export default function WorkflowSettingsPage() {
const { workflow, update } = useWorkflowContext();

function onSubmit(e) {
e.preventDefault();
try {
console.log('+++ onSubmit: ', e);
// TODO: on Submit, run update() on every available field.
// also, make sure the 'data-updaterule' rules are implemented.
} catch (err) {
console.error('WorkflowSettingsPageError: ', err);
}
return false;
}

function doUpdate(e) {
const key = e.target.name;
let value = e.target.value || '';
const { updaterule } = e.target.dataset;

if (updaterule === 'convert_to_number') value = parseInt(value);
if (updaterule === 'undefined_if_empty') value = value || undefined;

// TODO: due to onBlur={doUpdate}, keyboard navigation may accidentally cause the Workflow to update multiple times.
// One solution is to check if the new value is different from the existing one.

update({
[key]: value
// 'configuration.classifier_version': '2.0' // TODO: figure out if this needed.
});
}

function testUpdate(e) {
const key = e.target.name;
const value = e.target.value || '';
console.log('+++ testUpdate: ', key, value);
}

if (!workflow) return null;

return (
<form className="workflow-settings-page" onSubmit={onSubmit}>
<label htmlFor="display_name">
Workflow Name
<input
type="text"
name="display_name"
defaultValue={workflow?.display_name || ''}
onBlur={doUpdate}
/>
</label>

<div className="column-group col-1">
<fieldset>
<legend>Associated Subject Sets</legend>
<p>Choose the set of subjects you want to use for this workflow. Add subject sets in the Subject Sets tab.</p>
<p>TODO</p>
</fieldset>

<fieldset>
<legend>Associated Tutorial</legend>
<p>Choose the tutorials you want to use for this workflow. Create tutorials in the Tutorial tab.</p>
<p>TODO</p>
</fieldset>

<hr />

<fieldset>
<legend>Subject Retirement</legend>
<p id="subject-retirement-info">
Set how many people should classify each subject before it is
&quot;done.&quot; Once a subject has reached the retirement limit it
will no longer be shown to volunteers.
</p>
<div className="flex-row align-start spacing-bottom">
<select
aria-label="Retirement criteria"
className="flex-item"
defaultValue={workflow?.retirement?.criteria}
aria-describedby="subject-retirement-info"
name="retirement.criteria"
onChange={doUpdate}
>
<option value="classification_count">Classification count</option>
<option value="never_retire">Never retire</option>
{/* TODO: this is just a POC - never_retire should be removed, even though it's a valid option on the API. */}
</select>
<input
aria-label="Retirement count"
className="small-width"
defaultValue={workflow?.retirement?.options?.count}
data-updaterule="convert_to_number"
max="100"
min="1"
name="retirement.options.count"
onBlur={doUpdate}
placeholder="∞"
type="number"
/>
</div>
<p className="small-info">
If you&apos;d like more complex retirement rules such as conditional
retirement using Caesar, please get in touch via the Contact Us
page.
</p>
</fieldset>
</div>

<div className="column-group col-2">
<fieldset>
<legend>Subject Viewer</legend>
<p id="subject-viewer-info">
Choose how to display your subjects.
Refer to the Subject Viewer section of the Glossary for more info.
</p>
<div className="flex-row align-start spacing-bottom">
<select
aria-label="Subject viewer"
className="flex-item"
data-updaterule="undefined_if_empty"
defaultValue={workflow?.configuration?.subject_viewer || ''}
aria-describedby="subject-viewer-info"
name="configuration.subject_viewer"
onChange={doUpdate}
>
<option value="">None selected (default)</option>
<option value="imageAndText">Image and Text</option>
<option value="jsonData">JSON data charts</option>
<option value="multiFrame">Multi-Frame</option>
<option value="singleImage">Single Image</option>
<option value="singleText">Single Text</option>
<option value="subjectGroup">Subject Group</option>
</select>
</div>
</fieldset>

<fieldset className="disabled">
<legend>Multi-Image Options</legend>
<p>
Choose how to display subjects with multiple images.
If your subjects are in a sequence, such as camera trap images,
volunteers can play them like a .gif using the Flipbook viewer.
</p>
<p>TODO</p>
</fieldset>

<hr />

<fieldset>
<legend>Classification Tools</legend>
<p>TEST: HTML and styling for checkbox input</p>
<label
htmlFor="placeholder-1"
className="flex-row align-start spacing-bottom"
>
<input
type="checkbox"
id="placeholder-1"
name="placeholder-1"
onChange={testUpdate}
/>
<span>Placeholder 1</span>
</label>
<label
htmlFor="placeholder-2"
className="flex-row align-start spacing-bottom"
>
<input
defaultChecked={true}
type="checkbox"
id="placeholder-2"
name="placeholder-2"
onChange={testUpdate}
/>
<span>
Placeholder 2 -
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel tellus quam.
</span>
</label>
<label
htmlFor="placeholder-3"
className="flex-row align-start spacing-bottom"
>
<input
type="checkbox"
id="placeholder-3"
name="placeholder-3"
onChange={testUpdate}
/>
<span>
Placeholder 3 -
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel tellus quam.
Praesent lobortis dapibus nisi, ultricies blandit quam mattis vel.
Nunc consequat sem finibus, facilisis augue quis, euismod velit.
</span>
</label>
</fieldset>

<fieldset>
<legend>Quicktalk</legend>
<p>TEST: HTML and styling for radio options</p>
<label
className="flex-row align-start spacing-bottom"
htmlFor="placeholder-4-a"
>
<input
type="radio"
id="placeholder-4-a"
name="placeholder-4"
onChange={testUpdate}
value="Option A"
/>
<span>Placeholder 4, Option A</span>
</label>
<label
className="flex-row align-start spacing-bottom"
htmlFor="placeholder-4-b"
>
<input
defaultChecked={true}
type="radio"
id="placeholder-4-b"
name="placeholder-4"
onChange={testUpdate}
value="Option B"
/>
<span>Placeholder 4, Option B</span>
</label>
<label
className="flex-row align-start spacing-bottom"
htmlFor="placeholder-4-c"
>
<input
type="radio"
id="placeholder-4-c"
name="placeholder-4"
onChange={testUpdate}
value="Option C"
/>
<span>Placeholder 4, Option C</span>
</label>
</fieldset>

</div>
</form>
);
}
3 changes: 3 additions & 0 deletions app/pages/lab-pages-editor/icons/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Icons for the Pages Editor

Icons are organised into this folder, to make them easier should we choose to update them in the future - e.g. replace all FontAwesome icons with Grommet icons.
7 changes: 7 additions & 0 deletions app/pages/lab-pages-editor/icons/ReturnIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable react/react-in-jsx-scope */

export default function ReturnIcon() {
return (
<i className="icon fa fa-chevron-left" />
);
}
Loading

0 comments on commit 34f8fab

Please sign in to comment.