diff --git a/package-lock.json b/package-lock.json
index 340e43f12..2a9930a2a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8897,7 +8897,7 @@
},
"node_modules/data-model-navigator": {
"version": "1.1.34",
- "resolved": "git+ssh://git@github.com/CBIIT/Data-Model-Navigator.git#4b5c21a892c84178088541f9a31717d74f048087",
+ "resolved": "git+ssh://git@github.com/CBIIT/Data-Model-Navigator.git#84e0ab1c4238e1b6c89314f4af959a7af0fba511",
"license": "ISC",
"dependencies": {
"@material-ui/core": "^4.12.4",
diff --git a/src/assets/icons/Scroll_to_top.svg b/src/assets/icons/Scroll_to_top.svg
deleted file mode 100644
index aa39ff262..000000000
--- a/src/assets/icons/Scroll_to_top.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/src/components/ScrollButton/Up Arrow.svg b/src/assets/icons/arrow_up.svg
similarity index 100%
rename from src/components/ScrollButton/Up Arrow.svg
rename to src/assets/icons/arrow_up.svg
diff --git a/src/assets/icons/delete_single_file_icon.svg b/src/assets/icons/delete_single_file_icon.svg
deleted file mode 100644
index 4547d14b6..000000000
--- a/src/assets/icons/delete_single_file_icon.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/components/DataSubmissions/DataSubmissionListFilters.test.tsx b/src/components/DataSubmissions/DataSubmissionListFilters.test.tsx
index a1e859c81..a86a8aa2d 100644
--- a/src/components/DataSubmissions/DataSubmissionListFilters.test.tsx
+++ b/src/components/DataSubmissions/DataSubmissionListFilters.test.tsx
@@ -10,7 +10,6 @@ import {
ContextState as AuthContextState,
Status as AuthContextStatus,
} from "../Contexts/AuthContext";
-import { useOrganizationListContext } from "../Contexts/OrganizationListContext";
import { Column } from "../GenericTable";
import { ListSubmissionsResp } from "../../graphql";
@@ -82,28 +81,15 @@ describe("DataSubmissionListFilters Component", () => {
const submitterNames = ["Submitter1", "Submitter2"];
const dataCommons = ["DataCommon1", "DataCommon2"];
+ const organizations: Organization[] = [
+ { _id: "Org1", name: "Organization 1" } as Organization,
+ { _id: "Org2", name: "Organization 2" } as Organization,
+ ];
const columnVisibilityModel = { name: true, status: true };
const mockOnChange = jest.fn();
const mockOnColumnVisibilityModelChange = jest.fn();
- beforeEach(() => {
- // Set up default mock for useOrganizationListContext
- (useOrganizationListContext as jest.Mock).mockReturnValue({
- activeOrganizations: [
- { _id: "Org1", name: "Organization 1" },
- { _id: "Org2", name: "Organization 2" },
- ],
- status: "LOADED",
- data: {
- activeOrganizations: [
- { _id: "Org1", name: "Organization 1" },
- { _id: "Org2", name: "Organization 2" },
- ],
- },
- });
- });
-
afterEach(() => {
jest.clearAllMocks();
jest.useRealTimers();
@@ -114,6 +100,7 @@ describe("DataSubmissionListFilters Component", () => {
{
{
{
{
});
});
- it("prevents non-admin users from changing the organization select", async () => {
+ it("allows non-admin users to select an organization", async () => {
const { getByTestId } = render(
{
const organizationSelectInput = getByTestId("organization-select");
const button = within(organizationSelectInput).getByRole("button");
- expect(button).toHaveClass("Mui-readOnly");
+ expect(button).not.toHaveClass("Mui-readOnly");
});
it("resets all filters and clears URL searchParams when reset button is clicked", async () => {
@@ -250,6 +241,7 @@ describe("DataSubmissionListFilters Component", () => {
{
{
{
{
{
{
{
{
{
{
{
{
{
await waitFor(() => {
expect(getByTestId("data-commons-select-input")).toHaveValue("DataCommon1");
+ expect(mockOnChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ dataCommons: "DataCommon1",
+ })
+ );
});
-
- expect(mockOnChange).toHaveBeenCalledWith(
- expect.objectContaining({
- dataCommons: "DataCommon1",
- })
- );
});
it("sets submitterNames select to 'All' when submitterNames prop is empty", async () => {
@@ -826,6 +829,7 @@ describe("DataSubmissionListFilters Component", () => {
{
{
await waitFor(() => {
expect(getByTestId("submitter-name-select-input")).toHaveValue("Submitter1");
+ expect(mockOnChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ submitterName: "Submitter1",
+ })
+ );
});
+ });
- expect(mockOnChange).toHaveBeenCalledWith(
- expect.objectContaining({
- submitterName: "Submitter1",
- })
+ it("sets organization select to 'All' when organizations prop is empty", async () => {
+ const mockOnChange = jest.fn();
+ const mockOnColumnVisibilityModelChange = jest.fn();
+
+ const { getByTestId, getByRole } = render(
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(getByTestId("organization-select-input")).toHaveValue("All");
+ });
+
+ const organizationSelect = within(getByTestId("organization-select")).getByRole("button");
+ userEvent.click(organizationSelect);
+
+ const organizationList = within(getByRole("listbox", { hidden: true }));
+
+ await waitFor(() => {
+ expect(organizationList.getByTestId("organization-option-All")).toBeInTheDocument();
+ expect(organizationList.queryByTestId("organization-option-Org1")).not.toBeInTheDocument();
+ expect(organizationList.queryByTestId("organization-option-Org2")).not.toBeInTheDocument();
+ });
+ });
+
+ it("sets organization select to field.value when organizations prop is non-empty", async () => {
+ const mockOnChange = jest.fn();
+ const mockOnColumnVisibilityModelChange = jest.fn();
+
+ const { getByTestId, getByRole } = render(
+
+
+
);
+
+ await waitFor(() => {
+ expect(getByTestId("organization-select-input")).toHaveValue("All");
+ });
+
+ const organizationSelect = within(getByTestId("organization-select")).getByRole("button");
+ userEvent.click(organizationSelect);
+
+ const organizationList = within(getByRole("listbox", { hidden: true }));
+
+ await waitFor(() => {
+ expect(organizationList.getByTestId("organization-option-Org1")).toBeInTheDocument();
+ expect(organizationList.getByTestId("organization-option-Org2")).toBeInTheDocument();
+ });
+
+ userEvent.click(getByTestId("organization-option-Org1"));
+
+ await waitFor(() => {
+ expect(getByTestId("organization-select-input")).toHaveValue("Org1");
+ expect(mockOnChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ organization: "Org1",
+ })
+ );
+ });
});
});
diff --git a/src/components/DataSubmissions/DataSubmissionListFilters.tsx b/src/components/DataSubmissions/DataSubmissionListFilters.tsx
index cc4407c9e..fbaa9e4ad 100644
--- a/src/components/DataSubmissions/DataSubmissionListFilters.tsx
+++ b/src/components/DataSubmissions/DataSubmissionListFilters.tsx
@@ -6,10 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import StyledSelectFormComponent from "../StyledFormComponents/StyledSelect";
import StyledTextFieldFormComponent from "../StyledFormComponents/StyledOutlinedInput";
import ColumnVisibilityButton from "../GenericTable/ColumnVisibilityButton";
-import { useOrganizationListContext } from "../Contexts/OrganizationListContext";
import { ListSubmissionsInput, ListSubmissionsResp } from "../../graphql";
-import { useAuthContext } from "../Contexts/AuthContext";
-import { canViewOtherOrgRoles } from "../../config/AuthRoles";
import { Column } from "../GenericTable";
import { useSearchParamsContext } from "../Contexts/SearchParamsContext";
import Tooltip from "../Tooltip";
@@ -118,6 +115,7 @@ type TouchedState = { [K in FilterFormKey]: boolean };
type Props = {
columns: Column[];
+ organizations: Pick[];
submitterNames: string[];
dataCommons: string[];
columnVisibilityModel: ColumnVisibilityModel;
@@ -127,14 +125,13 @@ type Props = {
const DataSubmissionListFilters = ({
columns,
+ organizations,
submitterNames,
dataCommons,
columnVisibilityModel,
onColumnVisibilityModelChange,
onChange,
}: Props) => {
- const { user } = useAuthContext();
- const { activeOrganizations } = useOrganizationListContext();
const { searchParams, setSearchParams } = useSearchParamsContext();
const { control, register, watch, reset, setValue, getValues } = useForm({
defaultValues,
@@ -150,11 +147,13 @@ const DataSubmissionListFilters = ({
const [touchedFilters, setTouchedFilters] = useState(initialTouchedFields);
- const canViewOtherOrgs = canViewOtherOrgRoles.includes(user?.role);
const debounceAfter3CharsInputs: FilterFormKey[] = ["name", "dbGaPID"];
const debouncedOnChangeRef = useRef(
debounce((form: FilterForm) => handleFormChange(form), 500)
).current;
+ const debouncedDropdownRef = useRef(
+ debounce((form: FilterForm) => handleFormChange(form), 0)
+ ).current;
useEffect(() => {
// Reset submitterName filter if it is no longer a valid option
@@ -172,10 +171,22 @@ const DataSubmissionListFilters = ({
}, [submitterNames, submitterNameFilter, touchedFilters]);
useEffect(() => {
- if (!activeOrganizations?.length) {
- return;
+ // Reset organization filter if it is no longer a valid option
+ // due to other filters changing
+ const organizationIds = organizations?.map((org) => org._id);
+ if (
+ orgFilter !== "All" &&
+ Object.values(touchedFilters).some((filter) => filter) &&
+ !organizationIds?.includes(orgFilter)
+ ) {
+ const newSearchParams = new URLSearchParams(searchParams);
+ newSearchParams.delete("organization");
+ setSearchParams(newSearchParams);
+ setValue("organization", "All");
}
+ }, [organizations, orgFilter, touchedFilters]);
+ useEffect(() => {
const organizationId = searchParams.get("organization");
const status = searchParams.get("status");
const dataCommon = searchParams.get("dataCommons");
@@ -184,8 +195,10 @@ const DataSubmissionListFilters = ({
const submitterName = searchParams.get("submitterName");
handleStatusChange(status);
- handleOrganizationChange(organizationId);
+ if (organizationId && organizationId !== orgFilter) {
+ setValue("organization", organizationId);
+ }
if (dataCommon && dataCommon !== dataCommonsFilter) {
setValue("dataCommons", dataCommon);
}
@@ -202,13 +215,7 @@ const DataSubmissionListFilters = ({
if (Object.values(touchedFilters).every((filter) => !filter)) {
handleFormChange(getValues());
}
- }, [
- activeOrganizations,
- submitterNames,
- dataCommons,
- canViewOtherOrgs,
- searchParams?.toString(),
- ]);
+ }, [organizations, submitterNames, dataCommons, searchParams?.toString()]);
useEffect(() => {
if (Object.values(touchedFilters).every((filter) => !filter)) {
@@ -217,7 +224,7 @@ const DataSubmissionListFilters = ({
const newSearchParams = new URLSearchParams(searchParams);
- if (canViewOtherOrgs && orgFilter && orgFilter !== "All") {
+ if (orgFilter && orgFilter !== "All") {
newSearchParams.set("organization", orgFilter);
} else if (orgFilter === "All") {
newSearchParams.delete("organization");
@@ -264,6 +271,12 @@ const DataSubmissionListFilters = ({
useEffect(() => {
const subscription = watch((formValue: FilterForm, { name }) => {
+ const isDebouncedDropdown = ["submitterName", "dataCommons", "organization"].includes(name);
+ if (isDebouncedDropdown) {
+ debouncedDropdownRef(formValue);
+ return;
+ }
+
// Add debounce for input fields
const isDebounceField = debounceAfter3CharsInputs.includes(name as FilterFormKey);
// Debounce if value has at least 3 characters
@@ -293,28 +306,9 @@ const DataSubmissionListFilters = ({
};
}, [watch, debouncedOnChangeRef]);
- const isValidOrg = (orgId: string) =>
- orgId && !!activeOrganizations?.find((org) => org._id === orgId);
-
const isStatusFilterOption = (status: string): status is FilterForm["status"] =>
["All", ...statusValues].includes(status);
- const handleOrganizationChange = (organizationId: string) => {
- if (organizationId === orgFilter) {
- return;
- }
-
- if (
- !canViewOtherOrgs &&
- isValidOrg(user?.organization?.orgID) &&
- user?.organization?.orgID !== orgFilter
- ) {
- setValue("organization", user?.organization?.orgID);
- } else if (canViewOtherOrgs && isValidOrg(organizationId)) {
- setValue("organization", organizationId);
- }
- };
-
const handleStatusChange = (status: string) => {
if (status === statusFilter) {
return;
@@ -352,10 +346,7 @@ const DataSubmissionListFilters = ({
searchParams.delete("dbGaPID");
searchParams.delete("submitterName");
setSearchParams(newSearchParams);
- reset({
- ...defaultValues,
- organization: canViewOtherOrgs ? "All" : user?.organization?.orgID,
- });
+ reset({ ...defaultValues });
};
return (
@@ -371,14 +362,17 @@ const DataSubmissionListFilters = ({
render={({ field }) => (
org._id)?.includes(field.value)
+ ? field.value
+ : "All"
+ }
MenuProps={{ disablePortal: true }}
inputProps={{
id: "organization-filter",
"data-testid": "organization-select-input",
}}
data-testid="organization-select"
- readOnly={!canViewOtherOrgs}
onChange={(e) => {
field.onChange(e);
handleFilterChange("organization");
@@ -387,7 +381,7 @@ const DataSubmissionListFilters = ({
- {activeOrganizations?.map((org) => (
+ {organizations?.map((org) => (