Skip to content

Commit

Permalink
WasteLine form conditionals (#582)
Browse files Browse the repository at this point in the history
* adjust rollup chunkSize WarningLimit to 1000 kb

* convert dotHazardous field to controlled input

* convert epaWaste to controlled field

We need to be able to read the state of the field 'epaWaste' in order to
conditionally display/disable other fields.

* add zod validation schemas for dotInformation field and add as nested field in WasteLine

while the dotInformation is optional if the shipement is not dotHazardous, we've also added a refine method to the wasteline schema so if dotHazardous is true, both fields in dotInformation is required

* add static dotIdNumbers array temporaryily

* add error message when DOT ID number is not provided

* simplify wasteline zod refinements, remove old unused wasteline interfaces

* add wasteDescription validation

* add refinement that waste description is required if the waste is not DOT hazardous material

* update logic for dotHazardous and epaWaste switches

here's our logic, if dotHazardous then epaWaste can be true or false. However, if epaWaste is True then dotHazardous must be true

* add callback function to toggle epaWaste and dotHazardous with unit test

epaWaste and dotHazardous field depend on each other if epaWaste is true, then dotHazardous must be true. Converse, if dotHazardous is false, epaWaste must be false

* add integration test showing that when epaWaste is false, federal waste code select is disabled

* when epaWaste is set to false, all federal waste codes are removed from the wasteline
  • Loading branch information
dpgraham4401 authored Aug 30, 2023
1 parent 283b408 commit dc7c88d
Show file tree
Hide file tree
Showing 7 changed files with 2,684 additions and 151 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { HtForm } from 'components/Ht';
import { ManifestContext, ManifestContextType } from 'components/Manifest/ManifestForm';
import { StateWasteCodeSelect } from 'components/Manifest/WasteLine/HazardousWasteForm/StateWasteCodeSelect';
import { Code } from 'components/Manifest/WasteLine/wasteLineSchema';
import React, { useContext } from 'react';
import { Col, Row } from 'react-bootstrap';
import { Controller, useFormContext } from 'react-hook-form';
import Select, { components, GroupBase, MultiValueProps, StylesConfig } from 'react-select';
import { useGetFedWasteCodesQuery } from 'store/wasteCode.slice';
import { Code } from 'components/Manifest/WasteLine/wasteLineSchema';
import { ManifestContext, ManifestContextType } from 'components/Manifest/ManifestForm';
import { StateWasteCodeSelect } from 'components/Manifest/WasteLine/HazardousWasteForm/StateWasteCodeSelect';

interface HazardousWasteFormProps {
epaWaste: boolean;
}

/**
* Returns a form for adding waste code(s), to a wasteline, for a given manifest.
* It expects to be within the context of a manifest form.
* @constructor
*/
export function HazardousWasteForm() {
export function HazardousWasteForm({ epaWaste }: HazardousWasteFormProps) {
const { control } = useFormContext();
const { generatorStateCode, tsdfStateCode } = useContext<ManifestContextType>(ManifestContext);
// Retrieve federal waste codes from the server
Expand Down Expand Up @@ -69,6 +73,8 @@ export function HazardousWasteForm() {
<Select
id="hazardousWasteFederalWasteCodes"
{...field}
aria-label="Federal Waste Codes"
isDisabled={!epaWaste}
options={federalWasteCodes}
isLoading={federalLoading}
getOptionLabel={(option) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import Select, { components } from 'react-select';
import React, { useContext } from 'react';
import { useGetStateWasteCodesQuery } from 'store/wasteCode.slice';

interface StateWasteCodeSelectProps {
Expand All @@ -21,7 +21,9 @@ export function StateWasteCodeSelect({ stateId, fieldName }: StateWasteCodeSelec
hideSelectedOptions
placeholder={
<i className="text-muted">
Generator must be provided before adding origin state waste codes
{fieldName === 'hazardousWaste.generatorStateWasteCodes'
? 'Generator must be provided before adding generator state waste codes'
: 'TSDF must be provided before adding destination state waste codes'}
</i>
}
isDisabled={true}
Expand Down
109 changes: 109 additions & 0 deletions client/src/components/Manifest/WasteLine/WasteLineForm.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { renderWithProviders, screen } from 'test-utils';
import { describe, expect, test } from 'vitest';
import { WasteLineForm } from './WasteLineForm';

describe('WasteLineForm', () => {
test('renders', () => {
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
expect(screen.getByText(/General/i)).toBeInTheDocument();
// screen.debug(undefined, Infinity);
});
test('New waste lines set DOT haz material and EPA haz waste to true by default', () => {
// Arrange
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
const epaWasteSwitch = screen.getByRole('checkbox', { name: /EPA Hazardous Waste?/i });
const dotHazSwitch = screen.getByRole('checkbox', { name: /DOT Hazardous/i });
expect(epaWasteSwitch).toBeChecked();
expect(dotHazSwitch).toBeChecked();
});
test('Setting DOT hazardous to false automatically set EPA waste to false', async () => {
// Arrange
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
const epaWasteSwitch = await screen.findByRole('checkbox', { name: /EPA Hazardous Waste?/i });
const dotHazSwitch = await screen.findByRole('checkbox', { name: /DOT Hazardous/i });
// Act
await userEvent.click(dotHazSwitch);
// Assert
expect(epaWasteSwitch).not.toBeChecked();
expect(dotHazSwitch).not.toBeChecked();
});
test('Setting EPA waste to true automatically set DOT Hazardous to true', async () => {
// Arrange
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
const epaWasteSwitch = await screen.findByRole('checkbox', { name: /EPA Hazardous Waste?/i });
const dotHazSwitch = await screen.findByRole('checkbox', { name: /DOT Hazardous/i });
// first set both switches to false
await userEvent.click(epaWasteSwitch);
await userEvent.click(dotHazSwitch);
// Check they are both false (unchecked)
expect(epaWasteSwitch).not.toBeChecked();
expect(dotHazSwitch).not.toBeChecked();
// Act
await userEvent.click(epaWasteSwitch);
// Assert
expect(epaWasteSwitch).toBeChecked();
expect(dotHazSwitch).toBeChecked();
});
test('If epaWaste is false, federal waste code select is disabled', async () => {
// Arrange
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
const epaWasteSwitch = await screen.findByRole('checkbox', { name: /EPA Hazardous Waste?/i });
const federalWasteCodeSelect = await screen.findByLabelText('Federal Waste Codes');
// Act
await userEvent.click(epaWasteSwitch);
expect(epaWasteSwitch).not.toBeChecked(); // check EPA waste is false
// Assert
expect(federalWasteCodeSelect).toBeDisabled();
});
test('If epaWaste is false, federal waste codes are cleared', async () => {
// Arrange
renderWithProviders(
<WasteLineForm
appendWaste={() => console.log('waste appended')}
handleClose={() => console.log('close action handled')}
/>,
{}
);
const epaWasteSwitch = await screen.findByRole('checkbox', { name: /EPA Hazardous Waste?/i });
const federalWasteCodeSelect = await screen.findByLabelText('Federal Waste Codes');
await userEvent.type(federalWasteCodeSelect, 'D001');
expect(federalWasteCodeSelect).toHaveValue('D001');
// Act
await userEvent.click(epaWasteSwitch);
// Assert
expect(federalWasteCodeSelect).toHaveValue('');
});
});
159 changes: 131 additions & 28 deletions client/src/components/Manifest/WasteLine/WasteLineForm.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { HtCard, HtForm } from 'components/Ht';
import { AdditionalInfoForm } from 'components/AdditionalInfo/AdditionalInfoForm';
import { HazardousWasteForm } from 'components/Manifest/WasteLine/HazardousWasteForm';
import React from 'react';
import { Button, Container, Form, Row } from 'react-bootstrap';
import { FormProvider, UseFieldArrayAppend, useForm } from 'react-hook-form';
import { HtCard, HtForm } from 'components/Ht';
import { Manifest } from 'components/Manifest/manifestSchema';
import { dotIdNumbers } from 'components/Manifest/WasteLine/dotInfo';
import { HazardousWasteForm } from 'components/Manifest/WasteLine/HazardousWasteForm';
import { WasteLine, wasteLineSchema } from 'components/Manifest/WasteLine/wasteLineSchema';
import React from 'react';
import { Button, Col, Container, Form, Row } from 'react-bootstrap';
import { Controller, FormProvider, UseFieldArrayAppend, useForm } from 'react-hook-form';
import { QuantityForm } from './QuantityForm';

interface WasteLineFormProps {
Expand All @@ -17,7 +18,6 @@ interface WasteLineFormProps {

const wasteLineDefaultValues: Partial<WasteLine> = {
dotHazardous: true,
epaWaste: false,
// @ts-ignore
quantity: { containerNumber: 1, quantity: 1 },
};
Expand All @@ -29,11 +29,24 @@ const wasteLineDefaultValues: Partial<WasteLine> = {
*/
export function WasteLineForm({ handleClose, appendWaste, currentWastes }: WasteLineFormProps) {
const newLineNumber = currentWastes ? currentWastes.length + 1 : 1;
const [dotHazardous, setDotHazardous] = React.useState<boolean>(true);
const [epaWaste, setEpaWaste] = React.useState<boolean>(true);
const wasteMethods = useForm<WasteLine>({
resolver: zodResolver(wasteLineSchema),
defaultValues: { ...wasteLineDefaultValues, lineNumber: newLineNumber },
defaultValues: {
...wasteLineDefaultValues,
dotHazardous: dotHazardous,
epaWaste: epaWaste,
lineNumber: newLineNumber,
},
});
const { register, handleSubmit } = wasteMethods;
const {
register,
handleSubmit,
formState: { errors },
setValue,
getValues,
} = wasteMethods;

/**
* onSubmit is the callback function for the form submission.
Expand All @@ -42,6 +55,39 @@ export function WasteLineForm({ handleClose, appendWaste, currentWastes }: Waste
const onSubmit = (wasteLine: WasteLine) => {
appendWaste(wasteLine); // append the new waste line to the manifest
handleClose();
console.log('dotInformation', wasteLine.dotInformation);
};

/**
* toggleDotHazardous - set state and form value for DOT hazardous
* If DOT hazardous is set to false, then EPA waste must also be false.
* @param checked
*/
const toggleDotHazardous = (checked: boolean) => {
setValue('dotHazardous', checked);
setDotHazardous(checked);
if (!checked) {
setValue('epaWaste', false);
setEpaWaste(false);
}
};

/**
* toggleEpaWaste - set state and form value for EPA waste
* If EPA waste is set to true, then DOT hazardous must also be true.
* If EPA waste is set to false, then federal waste codes must be cleared.
* @param checked
*/
const toggleEpaWaste = (checked: boolean) => {
setValue('epaWaste', checked);
setEpaWaste(checked);
if (checked) {
setValue('dotHazardous', true);
setDotHazardous(true);
}
if (!checked) {
setValue('hazardousWaste.federalWasteCodes', []);
}
};

return (
Expand All @@ -53,18 +99,31 @@ export function WasteLineForm({ handleClose, appendWaste, currentWastes }: Waste
<h5>General Information</h5>
<Container className="ms-2">
<Row>
<HtForm.Switch
id="dotHazardousSwitch"
label="DOT Hazardous Material?"
{...register('dotHazardous')}
// autoFocus
<Controller
control={wasteMethods.control}
name={'dotHazardous'}
render={({ field }) => (
<HtForm.Switch
id="dotHazardousSwitch"
label="DOT Hazardous Material?"
{...register('dotHazardous')}
onChange={(e) => toggleDotHazardous(e.target.checked)}
/>
)}
/>
</Row>
<Row>
<HtForm.Switch
id="epaWasteSwitch"
label="EPA Hazardous Waste?"
{...register('epaWaste')}
<Controller
control={wasteMethods.control}
name={'epaWaste'}
render={({ field }) => (
<HtForm.Switch
id="epaWasteSwitch"
label="EPA Hazardous Waste?"
{...register('epaWaste')}
onChange={(e) => toggleEpaWaste(e.target.checked)}
/>
)}
/>
</Row>
<Row>
Expand All @@ -84,16 +143,60 @@ export function WasteLineForm({ handleClose, appendWaste, currentWastes }: Waste
/>
</Row>
</Container>
<Row>
<HtForm.Group>
<HtForm.Label htmlFor="wasteDescription">Waste Description</HtForm.Label>
<Form.Control
id="wasteDescription"
as="textarea"
{...register(`wasteDescription`)}
/>
</HtForm.Group>
</Row>
{!dotHazardous && (
<Row>
<HtForm.Group>
<HtForm.Label htmlFor="wasteDescription">Waste Description</HtForm.Label>
<Form.Control
id="wasteDescription"
as="textarea"
className={errors.wasteDescription && 'is-invalid'}
{...register(`wasteDescription`)}
/>
<div className="invalid-feedback">{errors.wasteDescription?.message}</div>
</HtForm.Group>
</Row>
)}
{dotHazardous && (
<Row>
<Col xs={9}>
<HtForm.Group>
<HtForm.Label htmlFor="dotDescription">DOT Description</HtForm.Label>
<Form.Control
id="dotDescription"
as="textarea"
{...register(`dotInformation.printedDotInformation`)}
className={errors.dotInformation?.printedDotInformation && 'is-invalid'}
/>
<div className="invalid-feedback">
{errors.dotInformation?.printedDotInformation?.message}
</div>
</HtForm.Group>
</Col>
<Col>
<HtForm.Group>
<HtForm.Label htmlFor="dotDescription">DOT ID Number</HtForm.Label>
<Form.Select
id="dotIdNumber"
as="input"
defaultValue={''}
{...register(`dotInformation.idNumber.code`)}
className={errors.dotInformation?.idNumber && 'is-invalid'}
>
<option value="">Select ID Number</option>
{dotIdNumbers.map((idNumber) => (
<option key={idNumber} value={idNumber}>
{idNumber}
</option>
))}
</Form.Select>
<div className="invalid-feedback">
{errors.dotInformation?.idNumber?.code?.message}
</div>
</HtForm.Group>
</Col>
</Row>
)}
</HtCard.Body>
</HtCard>
<HtCard border={'secondary'}>
Expand All @@ -108,7 +211,7 @@ export function WasteLineForm({ handleClose, appendWaste, currentWastes }: Waste
<HtCard.Body>
<h5>Waste Codes</h5>
<Row className="mb-2">
<HazardousWasteForm />
<HazardousWasteForm epaWaste={epaWaste} />
</Row>
</HtCard.Body>
</HtCard>
Expand Down
Loading

0 comments on commit dc7c88d

Please sign in to comment.