diff --git a/src/components/PermissionPanel/index.test.tsx b/src/components/PermissionPanel/index.test.tsx index 6c41f7de8..64d833704 100644 --- a/src/components/PermissionPanel/index.test.tsx +++ b/src/components/PermissionPanel/index.test.tsx @@ -77,6 +77,7 @@ describe("Accessibility", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: false, disabled: false, }, @@ -84,6 +85,7 @@ describe("Accessibility", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: true, disabled: true, }, @@ -93,6 +95,7 @@ describe("Accessibility", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, disabled: false, }, @@ -100,6 +103,7 @@ describe("Accessibility", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: false, disabled: false, }, @@ -195,6 +199,7 @@ describe("Basic Functionality", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -202,6 +207,7 @@ describe("Basic Functionality", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: true, disabled: true, }, @@ -209,6 +215,7 @@ describe("Basic Functionality", () => { _id: "program:manage", group: "Admin", name: "Manage Programs", + order: 0, checked: false, disabled: false, }, @@ -218,6 +225,7 @@ describe("Basic Functionality", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, disabled: false, }, @@ -225,6 +233,7 @@ describe("Basic Functionality", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: false, disabled: false, }, @@ -295,6 +304,7 @@ describe("Basic Functionality", () => { _id: "submission_request:create", group: "Group1", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -302,6 +312,7 @@ describe("Basic Functionality", () => { _id: "submission_request:submit", group: "Group1", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -309,6 +320,7 @@ describe("Basic Functionality", () => { _id: "submission_request:review", group: "Group2", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -316,6 +328,7 @@ describe("Basic Functionality", () => { _id: "submission_request:submit", group: "Group3", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -323,6 +336,7 @@ describe("Basic Functionality", () => { _id: "submission_request:view", group: "Group4", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -330,6 +344,7 @@ describe("Basic Functionality", () => { _id: "data_submission:create", group: "Group5", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -337,6 +352,7 @@ describe("Basic Functionality", () => { _id: "study:manage", group: "Group6", name: "Create", + order: 0, checked: true, disabled: false, }, @@ -413,6 +429,7 @@ describe("Basic Functionality", () => { _id: "access:requested", group: "Group1", name: "Notification 1", + order: 0, checked: true, disabled: false, }, @@ -420,6 +437,7 @@ describe("Basic Functionality", () => { _id: "data_submission:deleted", group: "Group1", name: "Notification 1-2", + order: 0, checked: true, disabled: false, }, @@ -427,6 +445,7 @@ describe("Basic Functionality", () => { _id: "account:disabled", group: "Group2", name: "Notification 2", + order: 0, checked: true, disabled: false, }, @@ -434,6 +453,7 @@ describe("Basic Functionality", () => { _id: "data_submission:cancelled", group: "Group3", name: "Notification 3", + order: 0, checked: true, disabled: false, }, @@ -441,6 +461,7 @@ describe("Basic Functionality", () => { _id: "submission_request:to_be_reviewed", group: "Group4", name: "Notification 4", + order: 0, checked: true, disabled: false, }, @@ -448,6 +469,7 @@ describe("Basic Functionality", () => { _id: "data_submission:withdrawn", group: "Group5", name: "Notification 5", + order: 0, checked: true, disabled: false, }, @@ -455,6 +477,7 @@ describe("Basic Functionality", () => { _id: "data_submission:deleted", group: "Group6", name: "Notification 6", + order: 0, checked: true, disabled: false, }, @@ -513,6 +536,112 @@ describe("Basic Functionality", () => { expect(queryByTestId("notifications-column-3")).not.toBeInTheDocument(); }); + it("should sort the permissions and notifications by their order property", async () => { + const mock: MockedResponse = { + request: { + query: RETRIEVE_PBAC_DEFAULTS, + variables: { roles: ["All"] }, + }, + result: { + data: { + retrievePBACDefaults: [ + { + role: "Submitter", + permissions: [ + { + _id: "submission_request:create", + group: "Group1", + name: "Create", + order: 1, + checked: true, + disabled: false, + }, + { + _id: "submission_request:submit", + group: "Group1", + name: "Submit", + order: 0, + checked: true, + disabled: false, + }, + { + _id: "submission_request:review", + group: "Group1", + name: "Review", + order: 2, + checked: true, + disabled: false, + }, + ], + notifications: [ + { + _id: "access:requested", + group: "Group1", + name: "Notification 1", + order: 1, + checked: true, + disabled: false, + }, + { + _id: "data_submission:deleted", + group: "Group1", + name: "Notification 1-2", + order: 0, + checked: true, + disabled: false, + }, + { + _id: "account:disabled", + group: "Group1", + name: "Notification 2", + order: 2, + checked: true, + disabled: false, + }, + ], + }, + ], + }, + }, + }; + + const mockWatcher = jest.fn().mockImplementation((field) => { + if (field === "role") { + return "Submitter"; + } + + return []; + }); + + const { getByTestId } = render(, { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + await waitFor(() => { + expect(getByTestId("permissions-group-Group1")).toBeInTheDocument(); + }); + + const permissionGroup = getByTestId("permissions-group-Group1"); + expect(permissionGroup.innerHTML.search("permission-submission_request:submit")).toBeLessThan( + permissionGroup.innerHTML.search("permission-submission_request:create") + ); + expect(permissionGroup.innerHTML.search("permission-submission_request:create")).toBeLessThan( + permissionGroup.innerHTML.search("permission-submission_request:review") + ); + + const notificationGroup = getByTestId("notifications-group-Group1"); + expect(notificationGroup.innerHTML.search("notification-data_submission:deleted")).toBeLessThan( + notificationGroup.innerHTML.search("notification-access:requested") + ); + expect(notificationGroup.innerHTML.search("notification-access:requested")).toBeLessThan( + notificationGroup.innerHTML.search("notification-account:disabled") + ); + }); + it("should show an error when unable to retrieve the default PBAC details (GraphQL)", async () => { const mock: MockedResponse = { request: { @@ -573,6 +702,7 @@ describe("Implementation Requirements", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: false, disabled: false, }, @@ -580,6 +710,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: false, disabled: false, }, @@ -587,6 +718,7 @@ describe("Implementation Requirements", () => { _id: "program:manage", group: "Admin", name: "Manage Programs", + order: 0, checked: false, disabled: false, }, @@ -596,6 +728,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, disabled: false, }, @@ -603,6 +736,7 @@ describe("Implementation Requirements", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: false, disabled: false, }, @@ -691,6 +825,7 @@ describe("Implementation Requirements", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: false, disabled: false, }, @@ -698,6 +833,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: false, disabled: false, }, @@ -707,6 +843,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, disabled: false, }, @@ -714,6 +851,7 @@ describe("Implementation Requirements", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: false, disabled: false, }, @@ -726,6 +864,7 @@ describe("Implementation Requirements", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: false, // Original submitter had this checked disabled: false, }, @@ -733,6 +872,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: true, disabled: false, }, @@ -742,6 +882,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, // Original submitter had this checked disabled: false, }, @@ -749,6 +890,7 @@ describe("Implementation Requirements", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: true, disabled: false, }, @@ -865,6 +1007,7 @@ describe("Implementation Requirements", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: true, disabled: true, }, @@ -872,6 +1015,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:view", group: "Data Submission", name: "View", + order: 0, checked: true, disabled: false, }, @@ -881,6 +1025,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: true, disabled: true, }, @@ -888,6 +1033,7 @@ describe("Implementation Requirements", () => { _id: "account:disabled", group: "Account", name: "Disabled", + order: 0, checked: true, disabled: false, }, @@ -1002,6 +1148,7 @@ describe("Implementation Requirements", () => { _id: "submission_request:create", group: "Submission Request", name: "Create", + order: 0, checked: false, disabled: false, }, @@ -1011,6 +1158,7 @@ describe("Implementation Requirements", () => { _id: "data_submission:cancelled", group: "Data Submissions", name: "Cancelled", + order: 0, checked: false, disabled: false, }, diff --git a/src/graphql/retrievePBACDefaults.ts b/src/graphql/retrievePBACDefaults.ts index f50b821ac..e210c8dd8 100644 --- a/src/graphql/retrievePBACDefaults.ts +++ b/src/graphql/retrievePBACDefaults.ts @@ -8,6 +8,7 @@ export const query = gql` _id group name + order checked disabled } @@ -15,6 +16,7 @@ export const query = gql` _id group name + order checked disabled } diff --git a/src/types/PBAC.d.ts b/src/types/PBAC.d.ts index 98640a2cf..798f802ba 100644 --- a/src/types/PBAC.d.ts +++ b/src/types/PBAC.d.ts @@ -77,6 +77,10 @@ type PBACDefault = { * @example "Manage Users" */ name: string; + /** + * The sort order of the PBAC object within its group. + */ + order: number; /** * Whether the PBAC object is checked for the role. */ diff --git a/src/utils/profileUtils.test.ts b/src/utils/profileUtils.test.ts index ac7b698f2..251806548 100644 --- a/src/utils/profileUtils.test.ts +++ b/src/utils/profileUtils.test.ts @@ -147,9 +147,10 @@ describe("userToCollaborator cases", () => { describe("columnizePBACGroups cases", () => { const baseDefault: PBACDefault = { - _id: "access:request", // This is not actually used by the util + _id: "access:request", // The _id field is not actually used by the util name: "", group: "", + order: 0, checked: false, disabled: false, }; @@ -295,4 +296,38 @@ describe("columnizePBACGroups cases", () => { expect(columnized[3][0].data).toEqual([{ ...baseDefault, name: "3", group: "Miscellaneous" }]); expect(columnized[3][1].data).toEqual([{ ...baseDefault, name: "6", group: "Random Group 1" }]); }); + + it("should sort the options within each group by their order", () => { + const pbacDefaults: PBACDefault[] = [ + { ...baseDefault, name: "entry 1", group: "Group01", order: 5 }, + { ...baseDefault, name: "entry 2", group: "Group01", order: 1 }, + { ...baseDefault, name: "entry 3", group: "Group01", order: 3 }, + { ...baseDefault, name: "entry 4", group: "Group01", order: 2 }, + { ...baseDefault, name: "entry 5", group: "Group01", order: 4 }, + ]; + + const columnized = utils.columnizePBACGroups(pbacDefaults); + expect(columnized[0][0].data).toEqual([ + { ...baseDefault, name: "entry 2", group: "Group01", order: 1 }, + { ...baseDefault, name: "entry 4", group: "Group01", order: 2 }, + { ...baseDefault, name: "entry 3", group: "Group01", order: 3 }, + { ...baseDefault, name: "entry 5", group: "Group01", order: 4 }, + { ...baseDefault, name: "entry 1", group: "Group01", order: 5 }, + ]); + }); + + it("should leave the order unchanged if 'order' is null", () => { + const pbacDefaults: PBACDefault[] = [ + { ...baseDefault, name: "entry 1", group: "Group01", order: null }, + { ...baseDefault, name: "entry 2", group: "Group01", order: null }, + { ...baseDefault, name: "entry 3", group: "Group01", order: null }, + ]; + + const columnized = utils.columnizePBACGroups(pbacDefaults); + expect(columnized[0][0].data).toEqual([ + { ...baseDefault, name: "entry 1", group: "Group01", order: null }, + { ...baseDefault, name: "entry 2", group: "Group01", order: null }, + { ...baseDefault, name: "entry 3", group: "Group01", order: null }, + ]); + }); }); diff --git a/src/utils/profileUtils.ts b/src/utils/profileUtils.ts index 221dc68d4..302a924da 100644 --- a/src/utils/profileUtils.ts +++ b/src/utils/profileUtils.ts @@ -73,6 +73,7 @@ export const columnizePBACGroups = ( return []; } + // Group the PBACDefaults by their group name const groupedData: Record[]> = {}; data.forEach((item) => { const groupName = typeof item?.group === "string" ? item.group : ""; @@ -83,9 +84,16 @@ export const columnizePBACGroups = ( groupedData[groupName].push(item); }); + // Sort the PBACDefaults within each group + Object.values(groupedData).forEach((options: PBACDefault[]) => { + options.sort((a: PBACDefault, b: PBACDefault) => (a?.order || 0) - (b?.order || 0)); + }); + + // Sort the groups by their partial group name const sortedGroups = Object.entries(groupedData); sortedGroups.sort(([a], [b]) => orderPBACGroups(a, b)); + // Columnize the groups const columns: ColumnizedPBACGroups = []; sortedGroups.forEach(([group, data], index) => { const groupIndex = index > colCount - 1 ? colCount - 1 : index;