-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add frontend support for Manage Users pagination #8371
Changes from 8 commits
f7d6075
a792c4b
7882003
3a698f0
812ad30
e6a3da8
9cb7122
0c83837
625bd39
1cb6838
485485f
e178422
ffe8864
1115978
75f3ee5
f95d192
a6ce8d3
9ad3181
31e1517
3c3ff4f
e47766c
3452eca
b5ee413
f23eb3c
c818e21
e44c7c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,6 +194,20 @@ void searchUsersAndStatusInCurrentOrgPaged_success() { | |
checkApiUserWithStatus(users.get(0), "[email protected]", "Bobberoo", UserStatus.ACTIVE); | ||
} | ||
|
||
@Test | ||
@WithSimpleReportOrgAdminUser | ||
void searchUsersAndStatusInCurrentOrgPaged_pageNumberOutOfBounds_success() { | ||
initSampleData(); | ||
ManageUsersPageWrapper usersPageWrapper = | ||
_service.getPagedUsersAndStatusInCurrentOrg(25, 10, "Will"); | ||
Page<ApiUserWithStatus> usersPage = usersPageWrapper.getPageContent(); | ||
List<ApiUserWithStatus> users = usersPage.stream().toList(); | ||
assertEquals(1, users.size()); | ||
assertEquals(6, usersPageWrapper.getTotalUsersInOrg()); | ||
checkApiUserWithStatus( | ||
users.get(0), "[email protected]", "Williams", UserStatus.ACTIVE); | ||
} | ||
|
||
@Test | ||
@WithSimpleReportOrgAdminUser | ||
void getUser_withAdminUser_withOktaMigrationDisabled_success() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ const Settings = () => { | |
path={"self-registration"} | ||
element={<ManageSelfRegistrationLinksContainer />} | ||
/> | ||
<Route path="/" element={<ManageUsersContainer />} /> | ||
<Route path="users/:pageNumber" element={<ManageUsersContainer />} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot we could also add something like the following:
to default it to the users page like before if someone were to navigate to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea! I just added this redirect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the urls we have configured for the Lighthouse checks since adding this new redirect confused it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooo ok so I just tested this with an org with multiple facilities and it prompts the user to select the facility dropdown twice... 😓 I'll look in to seeing how we can get this to work for multiple facilities... Feel free to remove this redirect since everything else works as expected and I don't this was even required but a nice to have! My bad, Mike! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if that's because we'd have to add the active facility id to the search params like we do in the ManageUsersContainer component. I can test that out and see if it works
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @emyl3 I'm redeploying to dev5 with a fix for this, although it will still prompt the user to select which facility but only once There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ooo wow ok! Thank you for that quick fix. That prompt happening once totally makes sense! |
||
</Routes> | ||
</div> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,11 @@ const SettingsNav = () => { | |
<nav className="prime-secondary-nav" aria-label="Secondary navigation"> | ||
<ul className="usa-nav__secondary-links prime-nav"> | ||
<li className="usa-nav__secondary-item"> | ||
<LinkWithQuery to={`/settings`} end className={classNameByActive}> | ||
<LinkWithQuery | ||
to={`/settings/users/1`} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there anywhere else we need to update the routing to this settings page? Like possibly from any email templates? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 ooo that's a good callout, Mike! Based on a quick search of the static site as well as our email templates in this repo I don't see anything linking to that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked with Jayna and we confirmed there isn't anything else that needs to be updated for the settings route |
||
end | ||
className={classNameByActive} | ||
> | ||
Manage users | ||
</LinkWithQuery> | ||
</li> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -271,6 +271,7 @@ let reactivateUserAndResetPassword: (obj: any) => Promise<any>; | |
let resetUserPassword: (obj: any) => Promise<any>; | ||
let resetUserMfa: (obj: any) => Promise<any>; | ||
let resendUserActivationEmail: (obj: any) => Promise<any>; | ||
let setDebouncedQueryString = jest.fn(); | ||
|
||
type TestContainerProps = { | ||
children: React.ReactNode; | ||
|
@@ -362,71 +363,20 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={updateUserName} | ||
updateUserEmail={updateUserEmail} | ||
currentPage={1} | ||
entriesPerPage={10} | ||
mpbrown marked this conversation as resolved.
Show resolved
Hide resolved
|
||
totalEntries={3} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={3} | ||
/> | ||
</TestContainer> | ||
); | ||
await waitForElementToBeRemoved(() => screen.queryByText("?, ?")); | ||
return { user: userEvent.setup(), ...renderControls }; | ||
}; | ||
|
||
it("is searchable", async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These search tests got moved to |
||
//given | ||
const { user } = await renderAndWaitForLoad(); | ||
|
||
//when | ||
const searchBox = screen.getByRole("searchbox", { | ||
name: /search by name/i, | ||
}); | ||
|
||
await user.type(searchBox, "john"); | ||
|
||
//then | ||
await waitFor(() => { | ||
expect( | ||
screen.getByRole("tab", { | ||
name: displayFullName("John", "", "Arthur"), | ||
}) | ||
).toBeInTheDocument(); | ||
}); | ||
await waitFor(() => { | ||
expect( | ||
screen.queryByText(displayFullName("Bob", "", "Bobberoo"), { | ||
exact: false, | ||
}) | ||
).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it("displays no results message for empty filtered list", async () => { | ||
//given | ||
const { user } = await renderAndWaitForLoad(); | ||
|
||
//when | ||
const searchBox = screen.getByRole("searchbox", { | ||
name: /search by name/i, | ||
}); | ||
await user.type(searchBox, "john wick"); | ||
|
||
//then | ||
await waitFor(() => { | ||
expect( | ||
screen.queryByText(displayFullName("Jane", "", "Doe"), { | ||
exact: false, | ||
}) | ||
).not.toBeInTheDocument(); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect( | ||
screen.queryByText(displayFullName("Bob", "", "Bobberoo"), { | ||
exact: false, | ||
}) | ||
).not.toBeInTheDocument(); | ||
}); | ||
|
||
expect(screen.getByText("No results found.")).toBeInTheDocument(); | ||
}); | ||
|
||
it("enables logged-in user's settings except deletion and roles", async () => { | ||
const { user } = await renderAndWaitForLoad(); | ||
const nameButton = screen.getByRole("tab", { | ||
|
@@ -773,6 +723,13 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={() => Promise.resolve()} | ||
updateUserEmail={() => Promise.resolve()} | ||
totalEntries={0} | ||
entriesPerPage={10} | ||
currentPage={1} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={0} | ||
/> | ||
</TestContainer> | ||
); | ||
|
@@ -783,8 +740,9 @@ describe("ManageUsers", () => { | |
|
||
it("fails gracefully when there are no users", async () => { | ||
await renderWithNoUsers(); | ||
const noUsers = await screen.findByText("no users", { exact: false }); | ||
expect(noUsers).toBeInTheDocument(); | ||
expect( | ||
screen.getByText("There are no users in this organization.") | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it("adds a user when zero users exist", async () => { | ||
|
@@ -835,6 +793,13 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={() => Promise.resolve()} | ||
updateUserEmail={() => Promise.resolve()} | ||
totalEntries={2} | ||
entriesPerPage={10} | ||
currentPage={1} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={2} | ||
/> | ||
</TestContainer> | ||
); | ||
|
@@ -875,6 +840,13 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={() => Promise.resolve()} | ||
updateUserEmail={() => Promise.resolve()} | ||
totalEntries={2} | ||
entriesPerPage={10} | ||
currentPage={1} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={2} | ||
/> | ||
</TestContainer> | ||
); | ||
|
@@ -916,6 +888,13 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={() => Promise.resolve()} | ||
updateUserEmail={() => Promise.resolve()} | ||
totalEntries={2} | ||
entriesPerPage={10} | ||
currentPage={1} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={2} | ||
/> | ||
</TestContainer> | ||
); | ||
|
@@ -1013,6 +992,13 @@ describe("ManageUsers", () => { | |
resendUserActivationEmail={resendUserActivationEmail} | ||
updateUserName={() => Promise.resolve()} | ||
updateUserEmail={() => Promise.resolve()} | ||
totalEntries={3} | ||
entriesPerPage={10} | ||
currentPage={1} | ||
debouncedQueryString={""} | ||
setDebouncedQueryString={setDebouncedQueryString} | ||
queryLoadingStatus={false} | ||
totalUsersInOrg={3} | ||
/> | ||
); | ||
render( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to handle this edge case because I encountered an error when I was on page 7 of all results and entered a search query. The app requested page 7 of the search results and the backend calculated the start index as a higher index than the end index. This caused an illegal argument error when attempting to create the sublist