-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into TP2000-1193--upgrade-to-django-4-2
- Loading branch information
Showing
39 changed files
with
1,873 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
common/static/common/js/components/WorkbasketUserAssignment/AssignUserForm.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React from 'react'; | ||
import { useEffect } from 'react'; | ||
import accessibleAutocomplete from 'accessible-autocomplete' | ||
|
||
|
||
function AssignUserForm({ assignmentType, users }) { | ||
const elementId = "users-select" | ||
const elementName = "users" | ||
const label = "Assign user" | ||
const hint = "Select a user to assign" | ||
|
||
useEffect(() => { | ||
const selectElement = document.getElementById(elementId); | ||
if (selectElement) | ||
accessibleAutocomplete.enhanceSelectElement( | ||
{ | ||
autoselect: false, | ||
defaultValue: "", | ||
minLength: 2, | ||
showAllValues: true, | ||
selectElement: selectElement | ||
} | ||
); | ||
}, []) | ||
|
||
return ( | ||
<> | ||
<form action={assignUsersUrl} method="POST" data-testid={"assign-user-form"}> | ||
<input type="hidden" value={CSRF_TOKEN} name="csrfmiddlewaretoken"/> | ||
<div className="govuk-form-group"> | ||
<label className="govuk-label" htmlFor={elementId}> | ||
{label} | ||
</label> | ||
<div id={`${elementName}-hint`} className="govuk-hint"> | ||
{hint} | ||
</div> | ||
<select | ||
required={true} | ||
id={elementId} | ||
name={elementName} | ||
data-testid='assign-user-select' | ||
> | ||
<option value="">Select a user</option> | ||
{users.map(user => | ||
<option key={user.pk} value={user.pk}>{user.name}</option> | ||
)} | ||
</select> | ||
</div> | ||
<div className="govuk-form-group"> | ||
<input type="hidden" name="assignment_type" value={assignmentType}/> | ||
</div> | ||
<button type="submit" className="govuk-button" data-prevent-double-click="true">Save</button> | ||
</form> | ||
</> | ||
) | ||
} | ||
|
||
export { AssignUserForm }; |
53 changes: 53 additions & 0 deletions
53
common/static/common/js/components/WorkbasketUserAssignment/UnassignUserForm.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React from 'react'; | ||
import { useEffect } from 'react'; | ||
import accessibleAutocomplete from 'accessible-autocomplete' | ||
|
||
|
||
function UnassignUserForm({ users }) { | ||
const elementId = "assignments-select" | ||
const elementName = "assignments" | ||
const label = "Unassign user" | ||
const hint = "Select a user to unassign" | ||
|
||
useEffect(() => { | ||
const selectElement = document.getElementById(elementId); | ||
if (selectElement) | ||
accessibleAutocomplete.enhanceSelectElement( | ||
{ | ||
autoselect: false, | ||
defaultValue: "", | ||
minLength: 2, | ||
showAllValues: true, | ||
selectElement: selectElement | ||
} | ||
); | ||
}, []) | ||
|
||
return ( | ||
<form action={unassignUsersUrl} method="POST" data-testid={"unassign-user-form"}> | ||
<input type="hidden" value={CSRF_TOKEN} name="csrfmiddlewaretoken"/> | ||
<div className="govuk-form-group"> | ||
<label className="govuk-label" htmlFor={elementId}> | ||
{label} | ||
</label> | ||
<div id={`${elementName}-hint`} className="govuk-hint"> | ||
{hint} | ||
</div> | ||
<select | ||
required={true} | ||
id={elementId} | ||
name={elementName} | ||
data-testid='unassign-user-select' | ||
> | ||
<option value="">Select a user</option> | ||
{users.map(user => | ||
<option key={user.pk} value={user.pk}>{user.name}</option> | ||
)} | ||
</select> | ||
</div> | ||
<button type="submit" className="govuk-button" data-prevent-double-click="true">Save</button> | ||
</form> | ||
) | ||
} | ||
|
||
export { UnassignUserForm }; |
151 changes: 151 additions & 0 deletions
151
common/static/common/js/components/WorkbasketUserAssignment/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import React from 'react'; | ||
import { useState } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
import { createRoot } from 'react-dom/client'; | ||
import { AssignUserForm } from './AssignUserForm' | ||
import { UnassignUserForm } from './UnassignUserForm' | ||
|
||
|
||
/** | ||
* Renders a button that opens/closes a form to assign/unassign users. | ||
* @param action The form's action (Assign or Unassign) | ||
* @param assignment The form's assignment type (workers or reviewers) | ||
* @param users The form's selectable users | ||
* @param buttonId The button's element ID | ||
* @param formId The form's element ID | ||
*/ | ||
function WorkbasketUserAssignment({ action, assignment, users, buttonId, formId }) { | ||
const [showForm, setShowForm] = useState(null); | ||
const assignmentType = assignment == "workers" ? "WORKBASKET_WORKER" : "WORKBASKET_REVIEWER"; | ||
|
||
const removeFormDiv = () => { | ||
const possibleFormDivs = [ | ||
document.getElementById("assign-workers-form"), | ||
document.getElementById("unassign-workers-form"), | ||
document.getElementById("assign-reviewers-form"), | ||
document.getElementById("unassign-reviewers-form"), | ||
]; | ||
possibleFormDivs.forEach(form => { | ||
if (form) { | ||
const assignmentRow = form.previousSibling; | ||
assignmentRow.classList.remove("govuk-summary-list__row--no-border"); | ||
form.remove(); | ||
} | ||
}); | ||
} | ||
|
||
const createFormDiv = () => { | ||
const formDiv = document.createElement("div"); | ||
formDiv.id = formId; | ||
formDiv.className = "govuk-!-margin-top-4"; | ||
const assignmentButton = document.getElementById(buttonId); | ||
const assignmentRow = assignmentButton.closest(".govuk-summary-list__row"); | ||
assignmentRow.classList.add("govuk-summary-list__row--no-border"); | ||
assignmentRow.after(formDiv); | ||
setShowForm(formDiv); | ||
} | ||
|
||
const handleClick = (e) => { | ||
e.preventDefault(); | ||
const isShown = document.getElementById(formId); | ||
removeFormDiv(); | ||
if(showForm && isShown) { | ||
setShowForm(null); | ||
return; | ||
} | ||
createFormDiv(); | ||
} | ||
|
||
function UserForm({ action }) { | ||
if (action === "Assign") | ||
return <AssignUserForm assignmentType={assignmentType} users={users}/>; | ||
else | ||
return <UnassignUserForm users={users}/>; | ||
} | ||
|
||
return ( | ||
<> | ||
<button | ||
type="button" | ||
onClick={handleClick} | ||
id={buttonId} | ||
data-testid={buttonId} | ||
className="govuk-link fake-link"> | ||
{action}<span className="govuk-visually-hidden"> {assignment}</span> | ||
</button> | ||
|
||
{showForm !== null && createPortal( | ||
<UserForm action={action}/>, | ||
showForm | ||
)} | ||
</> | ||
) | ||
} | ||
|
||
/** | ||
* Creates React roots in which to render WorkBasketUserAssignment components | ||
* for each assign/unassign link on the workbasket summary view. | ||
*/ | ||
function init() { | ||
const assignWorkersLink = document.getElementById("assign-workers"); | ||
if (assignWorkersLink) { | ||
const assignWorkers = createRoot(assignWorkersLink) | ||
assignWorkers.render( | ||
<WorkbasketUserAssignment | ||
action="Assign" | ||
assignment="workers" | ||
users={assignableUsers} | ||
buttonId="assign-workers" | ||
formId="assign-workers-form" | ||
/> | ||
); | ||
} | ||
|
||
const assignReviewersLink = document.getElementById("assign-reviewers"); | ||
if (assignReviewersLink) { | ||
const assignReviewers = createRoot(assignReviewersLink) | ||
assignReviewers.render( | ||
<WorkbasketUserAssignment | ||
action="Assign" | ||
assignment="reviewers" | ||
users={assignableUsers} | ||
buttonId="assign-reviewers" | ||
formId="assign-reviewers-form" | ||
/> | ||
); | ||
} | ||
|
||
const unassignWorkersLink = document.getElementById("unassign-workers"); | ||
if (unassignWorkersLink) { | ||
const unassignWorkers = createRoot(unassignWorkersLink) | ||
unassignWorkers.render( | ||
<WorkbasketUserAssignment | ||
action="Unassign" | ||
assignment="workers" | ||
users={assignedWorkers} | ||
buttonId="unassign-workers" | ||
formId="unassign-workers-form" | ||
/> | ||
); | ||
} | ||
|
||
const unassignReviewersLink = document.getElementById("unassign-reviewers"); | ||
if (unassignReviewersLink) { | ||
const unassignReviewers = createRoot(unassignReviewersLink) | ||
unassignReviewers.render( | ||
<WorkbasketUserAssignment | ||
action="Unassign" | ||
assignment="reviewers" | ||
users={assignedReviewers} | ||
buttonId="unassign-reviewers" | ||
formId="unassign-reviewers-form" | ||
/> | ||
); | ||
} | ||
} | ||
|
||
function setupWorkbasketUserAssignment() { | ||
document.addEventListener('DOMContentLoaded', init()); | ||
} | ||
|
||
export { setupWorkbasketUserAssignment, WorkbasketUserAssignment }; |
91 changes: 91 additions & 0 deletions
91
common/static/common/js/components/WorkbasketUserAssignment/tests/AssignUserForm.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import renderer from 'react-test-renderer'; | ||
import { fireEvent, render, screen } from "@testing-library/react"; | ||
import { AssignUserForm } from '../AssignUserForm'; | ||
|
||
|
||
const mockUsers = [ | ||
{ | ||
"pk": 1, | ||
"name": "User One", | ||
}, | ||
{ | ||
"pk": 2, | ||
"name": "User Two", | ||
}, | ||
{ | ||
"pk": 3, | ||
"name": "User Three", | ||
}, | ||
] | ||
|
||
describe(AssignUserForm, () => { | ||
const csrfToken = "abc123"; | ||
const assignUsersUrl = "/workbaskets/1/assign-users/"; | ||
|
||
beforeAll(() => { | ||
window.CSRF_TOKEN = csrfToken; | ||
window.assignUsersUrl = assignUsersUrl; | ||
}); | ||
|
||
afterAll(() => { | ||
delete window.CSRF_TOKEN; | ||
delete window.assignUsersUrl; | ||
}); | ||
|
||
it('renders form', () => { | ||
const component = renderer.create( | ||
<AssignUserForm | ||
assignmentType={"WORKBASKET_WORKER"} | ||
users={mockUsers} | ||
/> | ||
); | ||
|
||
let tree = component.toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
it('has assign users form action url', () => { | ||
render( | ||
<AssignUserForm | ||
assignmentType={"WORKBASKET_WORKER"} | ||
users={mockUsers} | ||
/> | ||
); | ||
|
||
expect(screen.getByTestId("assign-user-form")).toHaveAttribute('action', assignUsersUrl); | ||
}); | ||
|
||
it('does not submit when form is empty', () => { | ||
render( | ||
<AssignUserForm | ||
assignmentType={"WORKBASKET_WORKER"} | ||
users={mockUsers} | ||
/> | ||
); | ||
|
||
const mockSubmit = jest.fn(e => e.preventDefault()); | ||
screen.getByTestId("assign-user-form").onsubmit = mockSubmit | ||
|
||
fireEvent.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
expect(mockSubmit).not.toHaveBeenCalled(); | ||
}) | ||
|
||
it('submits with selected user', () => { | ||
render( | ||
<AssignUserForm | ||
assignmentType={"WORKBASKET_WORKER"} | ||
users={mockUsers} | ||
/> | ||
); | ||
|
||
const mockSubmit = jest.fn(e => e.preventDefault()); | ||
screen.getByTestId("assign-user-form").onsubmit = mockSubmit | ||
const input = screen.getByTestId('assign-user-select'); | ||
|
||
fireEvent.change(input, {target: {value: mockUsers[0].pk}}); | ||
fireEvent.click(screen.getByRole("button", { name: "Save" })); | ||
|
||
expect(mockSubmit).toHaveBeenCalled(); | ||
}) | ||
}) |
Oops, something went wrong.