Skip to content

Commit

Permalink
Merge pull request #606 from CBIIT/CRDCDH-2265
Browse files Browse the repository at this point in the history
CRDCDH-2265 Support multi-select status selection
  • Loading branch information
Alejandro-Vega authored Feb 3, 2025
2 parents 322ce55 + 368abe0 commit a1b1969
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 64 deletions.
65 changes: 42 additions & 23 deletions src/components/DataSubmissions/DataSubmissionListFilters.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,22 +267,6 @@ describe("DataSubmissionListFilters Component", () => {

userEvent.click(orgSelectList.getByTestId("organization-option-Org2"));

const statusSelect = within(getByTestId("status-select")).getByRole("button");

userEvent.click(statusSelect);

const statusSelectList = within(statusSelect.parentElement).getByRole("listbox", {
hidden: true,
});

await waitFor(() => {
expect(within(statusSelectList).getByTestId("status-option-All")).toBeInTheDocument();
expect(within(statusSelectList).getByTestId("status-option-New")).toBeInTheDocument();
expect(within(statusSelectList).getByTestId("status-option-Submitted")).toBeInTheDocument();
});

userEvent.click(within(statusSelectList).getByTestId("status-option-Submitted"));

const dataCommonsSelect = within(getByTestId("data-commons-select")).getByRole("button");

userEvent.click(dataCommonsSelect);
Expand Down Expand Up @@ -334,7 +318,7 @@ describe("DataSubmissionListFilters Component", () => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
organization: "Org2",
status: "Submitted",
status: expect.arrayContaining(["New", "Submitted"]),
name: "Test Submission",
dbGaPID: "12345",
submitterName: "Submitter1",
Expand All @@ -347,7 +331,9 @@ describe("DataSubmissionListFilters Component", () => {

await waitFor(() => {
expect(getByTestId("organization-select-input")).toHaveValue("All");
expect(getByTestId("status-select-input")).toHaveValue("All");
expect(getByTestId("status-select-input")).toHaveValue(
["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"].join(",")
);
expect(getByTestId("data-commons-select-input")).toHaveValue("All");
expect(getByTestId("submission-name-input")).toHaveValue("");
expect(getByTestId("dbGaPID-input")).toHaveValue("");
Expand All @@ -357,7 +343,7 @@ describe("DataSubmissionListFilters Component", () => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
organization: "All",
status: "All",
status: ["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"],
dataCommons: "All",
name: "",
dbGaPID: "",
Expand Down Expand Up @@ -647,7 +633,7 @@ describe("DataSubmissionListFilters Component", () => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
organization: "Org1",
status: "Submitted",
status: ["Submitted"],
dataCommons: "DataCommon1",
name: "Test",
dbGaPID: "123",
Expand All @@ -659,6 +645,37 @@ describe("DataSubmissionListFilters Component", () => {
userEvent.clear(getByTestId("submission-name-input"));
});

it("initializes study field based on searchParams and ignores invalid options", async () => {
const initialEntries = ["/?status=Deleted&status=RandomFakeStatus"];

const mockOnChange = jest.fn();
const mockOnColumnVisibilityModelChange = jest.fn();

const { getByTestId } = render(
<TestParent initialEntries={initialEntries} userRole="Admin">
<DataSubmissionListFilters
columns={columns}
organizations={organizations}
submitterNames={submitterNames}
dataCommons={dataCommons}
columnVisibilityModel={columnVisibilityModel}
onColumnVisibilityModelChange={mockOnColumnVisibilityModelChange}
onChange={mockOnChange}
/>
</TestParent>
);

await waitFor(() => {
expect(getByTestId("status-select-input")).toHaveValue("Deleted");
});

expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
status: ["Deleted"],
})
);
});

it("initializes form fields to default when searchParams are empty", async () => {
const initialEntries = ["/"];

Expand All @@ -681,7 +698,9 @@ describe("DataSubmissionListFilters Component", () => {

await waitFor(() => {
expect(getByTestId("organization-select-input")).toHaveValue("All");
expect(getByTestId("status-select-input")).toHaveValue("All");
expect(getByTestId("status-select-input")).toHaveValue(
["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"].join(",")
);
expect(getByTestId("data-commons-select-input")).toHaveValue("All");
expect(getByTestId("submission-name-input")).toHaveValue("");
expect(getByTestId("dbGaPID-input")).toHaveValue("");
Expand All @@ -691,7 +710,7 @@ describe("DataSubmissionListFilters Component", () => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
organization: "All",
status: "All",
status: ["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"],
dataCommons: "All",
name: "",
dbGaPID: "",
Expand Down Expand Up @@ -724,7 +743,7 @@ describe("DataSubmissionListFilters Component", () => {
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({
organization: "All",
status: "All",
status: ["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"],
dataCommons: "All",
name: "",
dbGaPID: "",
Expand Down
53 changes: 26 additions & 27 deletions src/components/DataSubmissions/DataSubmissionListFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { memo, useEffect, useRef, useState } from "react";
import { FormControl, IconButton, MenuItem, Grid, Box, styled, Stack } from "@mui/material";
import { debounce, isEqual } from "lodash";
import { debounce, isEqual, sortBy } from "lodash";
import RefreshIcon from "@mui/icons-material/Refresh";
import { Controller, useForm } from "react-hook-form";
import StyledSelectFormComponent from "../StyledFormComponents/StyledSelect";
Expand Down Expand Up @@ -85,17 +85,17 @@ const statusValues: SubmissionStatus[] = [
"New",
"In Progress",
"Submitted",
"Released",
"Withdrawn",
"Released",
"Rejected",
"Completed",
"Canceled",
"Deleted",
];

const defaultValues: FilterForm = {
export const defaultValues: FilterForm = {
organization: "All",
status: "All",
status: ["New", "In Progress", "Submitted", "Withdrawn", "Released", "Rejected"],
dataCommons: "All",
name: "",
dbGaPID: "",
Expand Down Expand Up @@ -188,17 +188,21 @@ const DataSubmissionListFilters = ({

useEffect(() => {
const organizationId = searchParams.get("program");
const status = searchParams.get("status");
const statuses = searchParams.getAll("status");
const dataCommon = searchParams.get("dataCommons");
const name = searchParams.get("name");
const dbGaPID = searchParams.get("dbGaPID");
const submitterName = searchParams.get("submitterName");

handleStatusChange(status);

if (organizationId && organizationId !== orgFilter) {
setValue("organization", organizationId);
}
if (statuses.length > 0 && !isArrayEqual(statuses, statusFilter)) {
const validStatuses = statuses.filter((status) =>
statusValues.includes(status as SubmissionStatus)
) as SubmissionStatus[];
setValue("status", validStatuses);
}
if (dataCommon && dataCommon !== dataCommonsFilter) {
setValue("dataCommons", dataCommon);
}
Expand Down Expand Up @@ -229,10 +233,15 @@ const DataSubmissionListFilters = ({
} else if (orgFilter === "All") {
newSearchParams.delete("program");
}
if (statusFilter && statusFilter !== "All") {
newSearchParams.set("status", statusFilter);
} else if (statusFilter === "All") {
if (statusFilter?.length > 0) {
newSearchParams.delete("status");
if (!isArrayEqual(statusFilter, defaultValues.status)) {
statusFilter.forEach((status) => {
newSearchParams.append("status", status);
});
}
} else {
newSearchParams.set("status", "");
}
if (dataCommonsFilter && dataCommonsFilter !== "All") {
newSearchParams.set("dataCommons", dataCommonsFilter);
Expand Down Expand Up @@ -306,19 +315,6 @@ const DataSubmissionListFilters = ({
};
}, [watch, debouncedOnChangeRef]);

const isStatusFilterOption = (status: string): status is FilterForm["status"] =>
["All", ...statusValues].includes(status);

const handleStatusChange = (status: string) => {
if (status === statusFilter) {
return;
}

if (isStatusFilterOption(status)) {
setValue("status", status);
}
};

const handleFormChange = (form: FilterForm) => {
if (!onChange || !form) {
return;
Expand Down Expand Up @@ -349,6 +345,8 @@ const DataSubmissionListFilters = ({
reset({ ...defaultValues });
};

const isArrayEqual = (a1: string[], a2: string[]) => isEqual(sortBy(a1), sortBy(a2));

return (
<StyledFilters data-testid="data-submission-list-filters">
<Stack direction="row" alignItems="center" gap="12px">
Expand Down Expand Up @@ -405,18 +403,19 @@ const DataSubmissionListFilters = ({
render={({ field }) => (
<StyledSelect
{...field}
value={field.value}
value={field.value || []}
MenuProps={{ disablePortal: true }}
inputProps={{ id: "status-filter", "data-testid": "status-select-input" }}
data-testid="status-select"
onChange={(e) => {
field.onChange(e);
handleFilterChange("status");
}}
renderValue={(selected: string[]) =>
selected?.length > 1 ? `${selected.length} statuses selected` : selected
}
multiple
>
<MenuItem value="All" data-testid="status-option-All">
All
</MenuItem>
{statusValues.map((value) => (
<MenuItem
key={`submission_status_${value}`}
Expand Down
14 changes: 3 additions & 11 deletions src/content/dataSubmissions/DataSubmissionsListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import TruncatedText from "../../components/TruncatedText";
import StyledTooltip from "../../components/StyledFormComponents/StyledTooltip";
import { useColumnVisibility } from "../../hooks/useColumnVisibility";
import DataSubmissionListFilters, {
defaultValues,
FilterForm,
} from "../../components/DataSubmissions/DataSubmissionListFilters";
import NavigatorLink from "../../components/DataSubmissions/NavigatorLink";
Expand Down Expand Up @@ -244,14 +245,7 @@ const ListingView: FC = () => {
const [dataCommons, setDataCommons] = useState<string[]>([]);
const [totalData, setTotalData] = useState<number>(0);
const tableRef = useRef<TableMethods>(null);
const filtersRef = useRef<FilterForm>({
organization: "All",
status: "All",
dataCommons: "All",
name: "",
dbGaPID: "",
submitterName: "All",
});
const filtersRef = useRef<FilterForm>({ ...defaultValues });

const [listSubmissions, { refetch }] = useLazyQuery<ListSubmissionsResp, ListSubmissionsInput>(
LIST_SUBMISSIONS,
Expand Down Expand Up @@ -282,7 +276,7 @@ const ListingView: FC = () => {
const { data: d, error } = await listSubmissions({
variables: {
organization: organization ?? "All",
status: status ?? "All",
status,
dataCommons: dc ?? "All",
submitterName: submitterName ?? "All",
name: name || undefined,
Expand Down Expand Up @@ -366,8 +360,6 @@ const ListingView: FC = () => {
padding="57px 0 0 25px"
body={
<StyledBannerBody direction="row" alignItems="center" justifyContent="flex-end">
{/* NOTE For MVP-2: Organization Owners are just Users */}
{/* Create a submission only available to org owners and submitters that have organizations assigned */}
<CreateDataSubmissionDialog onCreate={handleOnCreateSubmission} />
</StyledBannerBody>
}
Expand Down
7 changes: 4 additions & 3 deletions src/graphql/listSubmissions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { TypedDocumentNode } from "@apollo/client";
import gql from "graphql-tag";

export const query = gql`
export const query: TypedDocumentNode<Response, Input> = gql`
query listSubmissions(
$organization: String
$status: String
$status: [String]
$dataCommons: String
$name: String
$dbGaPID: String
Expand Down Expand Up @@ -57,7 +58,7 @@ export const query = gql`

export type Input = {
organization?: string;
status?: SubmissionStatus | "All";
status?: SubmissionStatus[];
dataCommons?: string;
name?: string;
dbGaPID?: string;
Expand Down

0 comments on commit a1b1969

Please sign in to comment.