Skip to content

Commit

Permalink
Improve approving-your-own-doc UX (#469)
Browse files Browse the repository at this point in the history
* Improve approving-your-own-doc UX

* Update document.hbs
  • Loading branch information
jeffdaley authored Jan 31, 2024
1 parent 907a178 commit 25fa3dc
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 11 deletions.
7 changes: 6 additions & 1 deletion web/app/components/document/sidebar.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@
<div class="space-y-2">
<Document::Sidebar::SectionHeader @title="Status" />
<div class="flex space-x-1">
<Doc::State @state={{@document.status}} @hideProgress={{true}} />
<Doc::State
data-test-doc-status
@state={{@document.status}}
@hideProgress={{true}}
/>
<Hds::Badge @text={{@document.docType}} />
</div>
</div>
Expand Down Expand Up @@ -374,6 +378,7 @@
<div class="flex items-start gap-2 px-3">

<Hds::Button
data-test-change-doc-status-button
@text={{this.moveToStatusButtonText}}
@size="medium"
@color={{this.moveToStatusButtonColor}}
Expand Down
25 changes: 20 additions & 5 deletions web/app/components/document/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ export default class DocumentSidebarComponent extends Component<DocumentSidebarC
} catch {}
});

approve = task(async () => {
approve = task(async (options?: { skipSuccessMessage: boolean }) => {
try {
await this.fetchSvc.fetch(
`/api/${this.configSvc.config.api_version}/approvals/${this.docID}`,
Expand All @@ -870,7 +870,9 @@ export default class DocumentSidebarComponent extends Component<DocumentSidebarC
headers: { "Content-Type": "application/json" },
},
);
this.showFlashSuccess("Done!", "Document approved");
if (!options?.skipSuccessMessage) {
this.showFlashSuccess("Done!", "Document approved");
}
} catch (error: unknown) {
this.maybeShowFlashError(error as Error, "Unable to approve");
throw error;
Expand Down Expand Up @@ -902,12 +904,25 @@ export default class DocumentSidebarComponent extends Component<DocumentSidebarC
this.refreshRoute();
});

changeDocumentStatus = task(async (status) => {
changeDocumentStatus = task(async (newStatus: string) => {
try {
if (
newStatus === "Approved" &&
this.args.document.approvers?.includes(this.args.profile.email) &&
!this.args.document.approvedBy?.includes(this.args.profile.email)
) {
// If the owner is an approver, process their approval first.
await this.approve.perform({ skipSuccessMessage: true });
}

await this.patchDocument.perform({
status: status,
status: newStatus,
});
this.showFlashSuccess("Done!", `Document status changed to "${status}"`);

this.showFlashSuccess(
"Done!",
`Document status changed to "${newStatus}"`,
);
} catch (error: unknown) {
this.maybeShowFlashError(
error as Error,
Expand Down
42 changes: 37 additions & 5 deletions web/mirage/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,28 @@ export default function (mirageConfig) {
return new Response(404, {}, {});
});

/**
* Used when approving a document.
* Adds the user's email to the `approvedBy` array.
*/
this.post("/approvals/:document_id", (schema, request) => {
const document = schema.document.findBy({
objectID: request.params.document_id,
});

if (document) {
if (!document.attrs.approvedBy?.includes(TEST_USER_EMAIL)) {
const approvedBy = document.attrs.approvedBy || [];
document.update({
approvedBy: [...approvedBy, TEST_USER_EMAIL],
});
}
return new Response(200, {}, document.attrs);
}

return new Response(404, {}, {});
});

/**
* Used by the AuthenticatedUserService to add and remove subscriptions.
*/
Expand Down Expand Up @@ -616,11 +638,6 @@ export default function (mirageConfig) {
* Used to confirm that an approver has access to a document.
*/
this.get("/people", (schema, request) => {
// This allows the test user to view docs they're an approver on.
if (request.queryParams.emails === TEST_USER_EMAIL) {
return new Response(200, {}, []);
}

if (request.queryParams.emails !== "") {
const emails = request.queryParams.emails.split(",");

Expand Down Expand Up @@ -870,6 +887,21 @@ export default function (mirageConfig) {
}
});

/**
* Used by the sidebar to update a document,
* e.g., to change a its status.
*/
this.patch("/documents/:document_id", (schema, request) => {
let document = schema.document.findBy({
objectID: request.params.document_id,
});
if (document) {
let attrs = JSON.parse(request.requestBody);
document.update(attrs);
return new Response(200, {}, document.attrs);
}
});

/*************************************************************************
*
* PUT requests
Expand Down
67 changes: 67 additions & 0 deletions web/tests/acceptance/authenticated/document-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ const CONTINUE_TO_DOCUMENT_BUTTON_SELECTOR =
const DOC_PUBLISHED_COPY_URL_BUTTON_SELECTOR =
"[data-test-doc-published-copy-url-button]";

const CHANGE_DOC_STATUS_BUTTON = "[data-test-change-doc-status-button]";
const DOC_STATUS = "[data-test-doc-status]";

const CUSTOM_STRING_FIELD_SELECTOR = "[data-test-custom-field-type='string']";
const CUSTOM_PEOPLE_FIELD_SELECTOR = "[data-test-custom-field-type='people']";
const EDITABLE_FIELD_SAVE_BUTTON_SELECTOR =
Expand Down Expand Up @@ -772,6 +775,70 @@ module("Acceptance | authenticated/document", function (hooks) {
assert.dom(CUSTOM_PEOPLE_FIELD_SELECTOR).hasText("None");
});

test(`you can move a doc into the "approved" status`, async function (this: AuthenticatedDocumentRouteTestContext, assert) {
this.server.create("document", {
objectID: 1,
status: "In-Review",
});

await visit("/document/1");

assert.dom(DOC_STATUS).hasText("In review");

await click(CHANGE_DOC_STATUS_BUTTON);

assert.dom(DOC_STATUS).hasText("Approved");

const doc = this.server.schema.document.first();

assert.equal(doc.attrs.status, "Approved");
});

test("you can approve your own doc", async function (this: AuthenticatedDocumentRouteTestContext, assert) {
this.server.create("document", {
objectID: 1,
status: "In-Review",
approvers: [TEST_USER_EMAIL],
});

await visit("/document/1");

await click(CHANGE_DOC_STATUS_BUTTON);

const doc = this.server.schema.document.first();

assert.true(doc.attrs.approvedBy?.includes(TEST_USER_EMAIL));

assert
.dom(`${APPROVERS_SELECTOR} li ${APPROVED_BADGE_SELECTOR}`)
.exists("the approver is badged with a check");

assert.equal(doc.attrs.status, "Approved");
});

test("owners can move a doc they previously approved from in-review to approved", async function (this: AuthenticatedDocumentRouteTestContext, assert) {
this.server.create("document", {
objectID: 1,
status: "In-Review",
approvers: [TEST_USER_EMAIL],
owners: [TEST_USER_EMAIL],
approvedBy: [TEST_USER_EMAIL],
});

await visit("/document/1");

await click(CHANGE_DOC_STATUS_BUTTON);

const doc = this.server.schema.document.first();

assert.equal(doc.attrs.status, "Approved");

assert
.dom(FLASH_MESSAGE_SELECTOR)
.exists({ count: 1 })
.hasAttribute("data-test-flash-notification-type", "success");
});

test("approvers who have approved a document are badged with a checkmark", async function (this: AuthenticatedDocumentRouteTestContext, assert) {
this.server.create("document", {
objectID: 1,
Expand Down

0 comments on commit 25fa3dc

Please sign in to comment.