Skip to content

Commit

Permalink
Manage AddSample form with react-hook-form and improve UX
Browse files Browse the repository at this point in the history
  • Loading branch information
axelboc committed Sep 19, 2023
1 parent c30a10b commit 3a917f4
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 84 deletions.
133 changes: 71 additions & 62 deletions ui/src/components/Tasks/AddSample.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import React from 'react';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';
import { Modal, ButtonToolbar, Button, Form } from 'react-bootstrap';
import { InputField, FieldsRow } from './fields';
import validate from './validate_add_sample';
import { Modal, ButtonToolbar, Button, Form, Row, Col } from 'react-bootstrap';
import { bindActionCreators } from 'redux';
import { addSamplesToList } from '../../actions/sampleGrid';
import { addSampleAndMount, addSamplesToQueue } from '../../actions/queue';
import { showList } from '../../actions/queueGUI';
import { useLocation } from 'react-router-dom';
import { useForm } from 'react-hook-form';

function getDefaultSampleData(params) {
let prefix = params.sampleName ? params.sampleName : 'noname';

if (params.proteinAcronym && params.sampleName) {
prefix = `${params.proteinAcronym}-${prefix}`;
}
const REQUIRED_MSG = 'This field is required';
const PATTERN = /^[\w+:-]*$/u;
const PATTERN_MSG = 'Characters allowed: A-Z a-z 0-9 _+:-';

function getSampleData(params) {
return {
...params,
type: 'Sample',
defaultPrefix: prefix,
defaultPrefix: `${params.proteinAcronym}-${params.sampleName}`,
location: 'Manual',
loadable: true,
tasks: [],
Expand All @@ -31,74 +27,97 @@ function AddSample(props) {
const {
show,
hide,
invalid,
handleSubmit,
addSamplesToList,
addSampleAndMount,
addSamplesToQueue,
showList,
} = props;

const { register, formState, handleSubmit, setFocus } = useForm();
const { isSubmitted, errors } = formState;

const { pathname } = useLocation();

useEffect(() => {
if (show) {
// Timeout required when creating new sample from "Samples" page
setTimeout(() => setFocus('sampleName'), 0);
}
}, [setFocus, show]);

function addAndMount(params) {
const sampleData = getDefaultSampleData(params);
const sampleData = getSampleData(params);
addSamplesToList([sampleData]);
addSampleAndMount(sampleData);
hide();

if (pathname === '/datacollection') {
if (pathname === '/' || pathname === '/datacollection') {
// Switch to mounted sample tab
showList('current');
}

hide();
}

function addAndQueue(params) {
const sampleData = getDefaultSampleData(params);
const sampleData = getSampleData(params);
addSamplesToList([sampleData]);
addSamplesToQueue([sampleData]);
hide();
}

return (
<Modal show={show} onHide={hide}>
<Form
onSubmit={(evt) => {
evt.preventDefault();
handleSubmit(addAndMount)();
}}
>
<Modal show={show} onHide={hide} data-default-styles>
<Form noValidate onSubmit={handleSubmit(addAndMount)}>
<Modal.Header closeButton>
<Modal.Title>New Sample</Modal.Title>
</Modal.Header>
<Modal.Body>
<FieldsRow>
<InputField
propName="sampleName"
autoFocus // eslint-disable-line jsx-a11y/no-autofocus
label="Sample Name"
col1="4"
col2="6"
/>
<InputField
propName="proteinAcronym"
label="Protein Acronym"
col1="4"
col2="6"
/>
</FieldsRow>
<Form.Group as={Row} className="mb-3" controlId="sampleName">
<Col sm={4}>
<Form.Label column>Sample name</Form.Label>
</Col>
<Col sm={8}>
<Form.Control
type="text"
{...register('sampleName', {
required: REQUIRED_MSG,
pattern: { value: PATTERN, message: PATTERN_MSG },
})}
isValid={isSubmitted && !errors.sampleName}
isInvalid={isSubmitted && !!errors.sampleName}
/>
<Form.Control.Feedback type="invalid">
{errors.sampleName?.message}
</Form.Control.Feedback>
</Col>
</Form.Group>
<Form.Group as={Row} className="mb-3" controlId="proteinAcronym">
<Col sm={4}>
<Form.Label column>Protein acronym</Form.Label>
</Col>
<Col sm={8}>
<Form.Control
type="text"
{...register('proteinAcronym', {
required: REQUIRED_MSG,
pattern: { value: PATTERN, message: PATTERN_MSG },
})}
isValid={isSubmitted && !errors.proteinAcronym}
isInvalid={isSubmitted && !!errors.proteinAcronym}
/>
<Form.Control.Feedback type="invalid">
{errors.proteinAcronym?.message}
</Form.Control.Feedback>
</Col>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<ButtonToolbar>
<Button className="me-3" type="submit" disabled={invalid}>
<Button className="me-3" type="submit">
Mount
</Button>
<Button
variant="outline-secondary"
disabled={invalid}
onClick={() => {
handleSubmit(addAndQueue)();
}}
onClick={handleSubmit(addAndQueue)}
>
Queue
</Button>
Expand All @@ -109,21 +128,11 @@ function AddSample(props) {
);
}

const AddSampleForm = reduxForm({
form: 'addsample',
validate,
})(AddSample);

const AddSampleContainer = connect(
(state) => ({
initialValues: { ...state.taskForm.taskData.parameters },
}),
(dispatch) => ({
addSamplesToList: bindActionCreators(addSamplesToList, dispatch),
addSamplesToQueue: bindActionCreators(addSamplesToQueue, dispatch),
addSampleAndMount: bindActionCreators(addSampleAndMount, dispatch),
showList: bindActionCreators(showList, dispatch),
}),
)(AddSampleForm);
const AddSampleContainer = connect(undefined, (dispatch) => ({
addSamplesToList: bindActionCreators(addSamplesToList, dispatch),
addSamplesToQueue: bindActionCreators(addSamplesToQueue, dispatch),
addSampleAndMount: bindActionCreators(addSampleAndMount, dispatch),
showList: bindActionCreators(showList, dispatch),
}))(AddSample);

export default AddSampleContainer;
21 changes: 0 additions & 21 deletions ui/src/components/Tasks/validate_add_sample.js

This file was deleted.

3 changes: 2 additions & 1 deletion ui/src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ input[type='color']:focus,
z-index: 10000;
}

.modal-dialog {
/* Allow opting out of this global override */
.modal-dialog:not([data-default-styles]) {
width: min-content;
max-width: 90%;
min-width: 50%;
Expand Down

0 comments on commit 3a917f4

Please sign in to comment.