Skip to content

Commit

Permalink
Wasteline table UI updates (#680)
Browse files Browse the repository at this point in the history
* quick fix for manifest status editable (it is editable if the manifest is saved as a draft)

* initial use collapsable list instead of table for displaying waste lines

* add WasteRowActions dropdown

* change coverage provider to v8 (instead of istanbul)

* export ContainterType type alias and use to remove typescript ts-ignores in quantity form

* fix alignment for wate line table items

* change waste and transporter action drop down options based on whether the details are open

* add test suite for QuantityForm

* export QuantityUOM types to fix ts-ignores in quantity form unit of measure selection

* allowing editing the manifest type for manifest that have not been saved to e-Manifest yet
  • Loading branch information
dpgraham4401 authored Jan 11, 2024
1 parent 923c895 commit 4b7ce6c
Show file tree
Hide file tree
Showing 18 changed files with 468 additions and 155 deletions.
15 changes: 15 additions & 0 deletions .idea/runConfigurations/Vitest_Coverage.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .idea/runConfigurations/Vitest_UI.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@types/react-dom": "^18.2.18",
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/coverage-istanbul": "^1.1.0",
"@vitest/coverage-v8": "^1.1.3",
"@vitest/ui": "^1.1.0",
"c8": "^8.0.1",
"esbuild": "0.19.10",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ManifestStatusSelect } from 'components/Manifest/GeneralInfo/ManifestStatusSelect';
import { ManifestTypeSelect } from 'components/Manifest/GeneralInfo/ManifestTypeSelect';
import { Manifest, SubmissionType } from 'components/Manifest/manifestSchema';
import { Manifest } from 'components/Manifest/manifestSchema';
import { HtForm, InfoIconTooltip } from 'components/UI';
import { useReadOnly } from 'hooks/manifest';
import React from 'react';
Expand All @@ -13,13 +13,6 @@ interface GeneralInfoFormProps {
isDraft?: boolean;
}

const submissionTypeOptions: Array<{ value: SubmissionType; label: string }> = [
{ value: 'FullElectronic', label: 'Electronic' },
{ value: 'Hybrid', label: 'Hybrid' },
{ value: 'DataImage5Copy', label: 'Data + Image' },
{ value: 'Image', label: 'Image Only' },
];

export function GeneralInfoForm({ manifestData, isDraft }: GeneralInfoFormProps) {
const [readOnly] = useReadOnly();
const manifestForm = useFormContext<Manifest>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { ManifestStatusSelect } from 'components/Manifest/GeneralInfo/ManifestStatusSelect';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { cleanup, renderWithProviders, screen } from 'test-utils';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
import userEvent from '@testing-library/user-event';
import { setupServer } from 'msw/node';
import { userApiMocks } from 'test-utils/mock';
import { http, HttpResponse } from 'msw';
import { createMockHandler, createMockSite } from 'test-utils/fixtures';
import { createMockProfileResponse } from 'test-utils/fixtures/mockUser';
import { userApiMocks } from 'test-utils/mock';
import { API_BASE_URL } from 'test-utils/mock/htApiMocks';
import { createMockHandler, createMockSite } from 'test-utils/fixtures';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';

const server = setupServer(...userApiMocks);
afterEach(() => cleanup());
Expand Down Expand Up @@ -37,6 +37,7 @@ describe('Manifest Status Field', () => {
});
test('is not editable if read only', () => {
renderWithProviders(<TestComponent isDraft={true} readOnly={true} />);

expect(screen.getByLabelText(/Status/i)).toBeDisabled();
});
test('is editable if the manifest is a draft', () => {
Expand Down Expand Up @@ -121,4 +122,38 @@ describe('Manifest Status Field', () => {
expect(scheduledOption).toBeInTheDocument();
expect(scheduledOption).toHaveAttribute('aria-disabled', 'true');
});
test('is editable if draft status', async () => {
// Arrange
const userGeneratorSite = createMockSite({
handler: createMockHandler({
siteType: 'Generator',
epaSiteId: 'MOCKVAGEN001',
}),
});
server.use(
http.get(`${API_BASE_URL}/api/user/profile`, () => {
return HttpResponse.json(
{
...createMockProfileResponse({
sites: [
{
site: userGeneratorSite,
eManifest: 'signer',
},
],
}),
},
{ status: 200 }
);
})
);
renderWithProviders(<TestComponent isDraft={true} readOnly={false} />, {
preloadedState: { manifest: { readOnly: false } },
useFormProps: { values: { status: 'NotAssigned', generator: userGeneratorSite.handler } },
});
await userEvent.click(screen.getByLabelText(/Status/i));
const scheduledOption = screen.queryByRole('option', { name: /Scheduled/i });
expect(scheduledOption).toBeInTheDocument();
expect(scheduledOption).toHaveAttribute('aria-disabled', 'true');
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Manifest, ManifestStatus } from 'components/Manifest/manifestSchema';
import { HtForm, InfoIconTooltip } from 'components/UI';
import { useManifestStatus } from 'hooks/manifest';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { useGetProfileQuery } from 'store';
import { manifest } from 'services';
import Select, { SingleValue } from 'react-select';
import { useManifestStatus } from 'hooks/manifest';
import { manifest } from 'services';
import { useGetProfileQuery } from 'store';

interface StatusOption {
value: ManifestStatus;
Expand Down Expand Up @@ -43,6 +43,18 @@ export function ManifestStatusSelect({ readOnly, isDraft }: ManifestStatusFieldP
})
: [];

// Whether the status field should be disabled
const isStatusDisabled = (
status: ManifestStatus | undefined,
readOnly: boolean | undefined,
isDraft: boolean | undefined
) => {
if (readOnly) return true; // Read only manifests can never be edited
if (isDraft) return false; // Draft manifests can always be edited if not read only
if (status === 'NotAssigned') return false; // if editing a previously saved 'NotAssigned', allow editing
return true;
};

return (
<HtForm.Group>
<HtForm.Label htmlFor="status" className="mb-0">
Expand All @@ -61,7 +73,7 @@ export function ManifestStatusSelect({ readOnly, isDraft }: ManifestStatusFieldP
aria-label="status"
value={selectedStatus}
options={statusOptions}
isDisabled={readOnly || !isDraft}
isDisabled={isStatusDisabled(status, readOnly, isDraft)}
data-testid="manifestStatus"
isLoading={profileLoading || !profile}
onChange={(option: SingleValue<StatusOption>) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { ManifestTypeSelect } from 'components/Manifest/GeneralInfo/ManifestTypeSelect';
import { setupServer } from 'msw/node';
import React from 'react';
import { cleanup, renderWithProviders, screen } from 'test-utils';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';
import { setupServer } from 'msw/node';
import { userApiMocks } from 'test-utils/mock';
import { ManifestTypeSelect } from 'components/Manifest/GeneralInfo/ManifestTypeSelect';
import userEvent from '@testing-library/user-event';
import { createMockHandler } from 'test-utils/fixtures';
import { userApiMocks } from 'test-utils/mock';
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vitest';

const server = setupServer(...userApiMocks);
afterEach(() => cleanup());
Expand Down Expand Up @@ -62,4 +62,18 @@ describe('Manifest Type Field', () => {
'false'
);
});
test('is never editable if past draft status', async () => {
renderWithProviders(<TestComponent isDraft={false} readOnly={false} />, {
useFormProps: {
values: {
status: 'Scheduled',
generator: createMockHandler({
canEsign: true,
siteType: 'Generator',
}),
},
},
});
expect(screen.getByLabelText(/Type/i)).toBeDisabled();
});
});
20 changes: 17 additions & 3 deletions client/src/components/Manifest/GeneralInfo/ManifestTypeSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Manifest, SubmissionType } from 'components/Manifest/manifestSchema';
import { Manifest, ManifestStatus, SubmissionType } from 'components/Manifest/manifestSchema';
import { HtForm } from 'components/UI';
import { useManifestStatus } from 'hooks/manifest';
import React, { useState } from 'react';
import Select, { SingleValue } from 'react-select';
import { Controller, useFormContext } from 'react-hook-form';
import Select, { SingleValue } from 'react-select';

interface SubmissionTypeOption {
value: SubmissionType;
Expand All @@ -25,11 +26,24 @@ export function ManifestTypeSelect({
isDraft?: boolean;
}) {
const manifestForm = useFormContext<Manifest>();
const [status] = useManifestStatus();
const generatorCanESign = manifestForm.getValues('generator.canEsign');
const [submissionType, setSubmissionType] = useState<SubmissionType>(
manifestForm.getValues('submissionType') || 'Hybrid'
);
const selectedType = submissionTypeOptions.filter((option) => option.value === submissionType);

const isTypeDisabled = (
readOnly: boolean | undefined,
isDraft: boolean | undefined,
status: ManifestStatus | undefined
) => {
if (readOnly) return true; // Read only manifests can never be edited
if (isDraft) return false; // Draft manifests can always be edited if not read only
if (status === 'NotAssigned') return false; // if editing a previously saved 'NotAssigned', allow editing
return true;
};

return (
<HtForm.Group>
<HtForm.Label htmlFor="submissionType" className="mb-0">
Expand All @@ -43,7 +57,7 @@ export function ManifestTypeSelect({
{...field}
id="submissionType"
aria-label="submission type"
isDisabled={readOnly || !isDraft}
isDisabled={isTypeDisabled(readOnly, isDraft, status)}
value={selectedType}
options={submissionTypeOptions}
defaultValue={submissionTypeOptions[0]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import {
faArrowDown,
faArrowUp,
faEllipsisVertical,
faEye,
faEyeSlash,
faTrash,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import React, { MouseEventHandler, ReactElement } from 'react';
import { Col, Dropdown, Row } from 'react-bootstrap';
import React, { MouseEventHandler, ReactElement, useState } from 'react';
import { Col, Dropdown, Row, useAccordionButton } from 'react-bootstrap';
import { UseFieldArrayRemove, UseFieldArraySwap } from 'react-hook-form';

interface TranRowActionProps {
index: number;
length: number;
removeTransporter: UseFieldArrayRemove;
swapTransporter: UseFieldArraySwap;
eventKey: string;
}

interface RowDropdownItems {
Expand All @@ -34,9 +37,12 @@ export function TransporterRowActions({
removeTransporter,
swapTransporter,
length,
eventKey,
}: TranRowActionProps) {
const isFirst = index === 0;
const isLast = index + 1 === length;
const [open, setOpen] = useState(false);
const decoratedOnClick = useAccordionButton(eventKey, () => setOpen(!open));

const actions: RowDropdownItems[] = [
{
Expand Down Expand Up @@ -73,6 +79,15 @@ export function TransporterRowActions({
disabled: false,
label: `remove transporter ${index}`,
},
{
text: open ? 'Close' : 'Details',
icon: <FontAwesomeIcon icon={open ? faEyeSlash : faEye} className="text-info" />,
onClick: (event) => {
decoratedOnClick(event);
},
disabled: false,
label: `View transporter ${index} details`,
},
];

return (
Expand Down
Loading

0 comments on commit 4b7ce6c

Please sign in to comment.