diff --git a/CHANGELOG b/CHANGELOG
index 190688108..9dcb62be6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added patron ineligibility error messaging to HoldRequestErrorBanner component (SCC-3762)
- Added eligibility checks to EDD and on-site request hold pages and API routes (SCC-3762)
- Added HoldContactButton component and update error copy per VQA requests (SCC-4427)
+- Added email address field prepopulation on EDD Request form (SCC-4407)
### Updated
diff --git a/__test__/pages/hold/eddRequestPage.test.tsx b/__test__/pages/hold/eddRequestPage.test.tsx
index 160e4c251..37ae9b719 100644
--- a/__test__/pages/hold/eddRequestPage.test.tsx
+++ b/__test__/pages/hold/eddRequestPage.test.tsx
@@ -25,6 +25,7 @@ jest.mock("../../../src/server/auth")
jest.mock("../../../src/server/api/bib")
jest.mock("../../../src/server/sierraClient")
jest.mock("../../../src/server/api/hold")
+jest.mock("../../../src/models/MyAccount")
jest.mock("next/router", () => jest.requireActual("next-router-mock"))
@@ -203,6 +204,22 @@ describe("EDD Request page", () => {
expect(screen.getByTestId("edd-request-form")).toBeInTheDocument()
})
})
+ describe("EDD Request prepopulated form fields", () => {
+ beforeEach(() => {
+ render(
+
+ )
+ })
+ it("prepopulates the email field with the patron's email address if present", () => {
+ expect(screen.getByDisplayValue("test@test.com")).toBeInTheDocument()
+ })
+ })
describe("EDD Request form validation", () => {
beforeEach(async () => {
render(
diff --git a/pages/hold/request/[id]/edd.tsx b/pages/hold/request/[id]/edd.tsx
index 547c9dbdb..9a99cc545 100644
--- a/pages/hold/request/[id]/edd.tsx
+++ b/pages/hold/request/[id]/edd.tsx
@@ -7,6 +7,8 @@ import {
SkeletonLoader,
} from "@nypl/design-system-react-components"
+import sierraClient from "../../../../src/server/sierraClient"
+
import Layout from "../../../../src/components/Layout/Layout"
import EDDRequestForm from "../../../../src/components/HoldPages/EDDRequestForm"
import HoldRequestErrorBanner from "../../../../src/components/HoldPages/HoldRequestErrorBanner"
@@ -16,6 +18,8 @@ import { SITE_NAME, BASE_URL, PATHS } from "../../../../src/config/constants"
import useLoading from "../../../../src/hooks/useLoading"
import { fetchBib } from "../../../../src/server/api/bib"
+import MyAccount from "../../../../src/models/MyAccount"
+
import {
fetchDeliveryLocations,
fetchPatronEligibility,
@@ -43,6 +47,7 @@ interface EDDRequestPropsType {
discoveryBibResult: DiscoveryBibResult
discoveryItemResult: DiscoveryItemResult
patronId: string
+ patronEmail?: string
isAuthenticated?: boolean
errorStatus?: HoldErrorStatus
patronEligibilityStatus?: PatronEligibilityStatus
@@ -55,12 +60,12 @@ export default function EDDRequestPage({
discoveryBibResult,
discoveryItemResult,
patronId,
+ patronEmail,
isAuthenticated,
errorStatus: defaultErrorStatus,
patronEligibilityStatus: defaultEligibilityStatus,
}: EDDRequestPropsType) {
const metadataTitle = `Electronic Delivery Request | ${SITE_NAME}`
-
const bib = new Bib(discoveryBibResult)
const item = new Item(discoveryItemResult, bib)
@@ -71,10 +76,11 @@ export default function EDDRequestPage({
defaultEligibilityStatus
)
- const [eddFormState, setEddFormState] = useState({
+ const [eddFormState, setEddFormState] = useState({
...initialEDDFormState,
+ emailAddress: patronEmail,
patronId,
- source: item.source,
+ source: item.formattedSourceForHoldRequest,
})
const [formPosting, setFormPosting] = useState(false)
@@ -251,6 +257,12 @@ export async function getServerSideProps({ params, req, res }) {
const patronEligibilityStatus = await fetchPatronEligibility(patronId)
+ // fetch patron's email to pre-populate the edd form if available
+ const client = await sierraClient()
+ const patronAccount = new MyAccount(client, patronId)
+ const patron = await patronAccount.getPatron()
+ const patronEmail = patron?.emails?.[0]
+
const locationOrEligibilityFetchFailed =
locationStatus !== 200 ||
![200, 401].includes(patronEligibilityStatus?.status)
@@ -260,6 +272,7 @@ export async function getServerSideProps({ params, req, res }) {
discoveryBibResult,
discoveryItemResult,
patronId,
+ patronEmail,
isAuthenticated,
patronEligibilityStatus,
errorStatus: locationOrEligibilityFetchFailed
diff --git a/pages/hold/request/[id]/index.tsx b/pages/hold/request/[id]/index.tsx
index e0b0577df..e6c223859 100644
--- a/pages/hold/request/[id]/index.tsx
+++ b/pages/hold/request/[id]/index.tsx
@@ -180,8 +180,8 @@ export default function HoldRequestPage({
handleSubmit={handleSubmit}
holdId={holdId}
patronId={patronId}
- source={item.source}
errorStatus={errorStatus}
+ source={item.formattedSourceForHoldRequest}
/>
>
) : null}
diff --git a/src/models/Item.ts b/src/models/Item.ts
index d3c8c8de7..ae4e81a88 100644
--- a/src/models/Item.ts
+++ b/src/models/Item.ts
@@ -12,6 +12,7 @@ import {
locationEndpointsMap,
} from "../utils/itemUtils"
import { appConfig } from "../config/config"
+import { convertCamelToShishKabobCase } from "../utils/appUtils"
/**
* The Item class contains the data and getter functions
@@ -81,6 +82,10 @@ export default class Item {
.includes("all")
}
+ get formattedSourceForHoldRequest(): string {
+ return convertCamelToShishKabobCase(this.source)
+ }
+
// Pre-processing logic for setting Item holding location
getLocationFromItem(item: DiscoveryItemResult): ItemLocation {
let location = defaultNYPLLocation
diff --git a/src/models/modelTests/Item.test.ts b/src/models/modelTests/Item.test.ts
index db080df42..d07e92901 100644
--- a/src/models/modelTests/Item.test.ts
+++ b/src/models/modelTests/Item.test.ts
@@ -104,6 +104,10 @@ describe("Item model", () => {
"A history of spaghetti eating and cooking for: spaghetti dinner."
)
})
+
+ it("returns the source in kebabcase for use in hold requests", () => {
+ expect(item.formattedSourceForHoldRequest).toBe("sierra-nypl")
+ })
})
describe("ReCAP checks", () => {
diff --git a/src/utils/appUtils.ts b/src/utils/appUtils.ts
index 67a1f8dbb..41a4c9e28 100644
--- a/src/utils/appUtils.ts
+++ b/src/utils/appUtils.ts
@@ -140,3 +140,19 @@ export const convertToSentenceCase = (str: string) =>
str.split(" ").length > 1
? str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()
: str
+
+/**
+ * Converts camel case string to shish kabob case
+ *
+ * e.g. camelToShishKabobCase("RecapPul")
+ * => "recap-pul"
+ * camelToShishKabobCase("firstCharCanBeLowerCase")
+ * => "first-char-can-be-lower-case"
+ */
+export const convertCamelToShishKabobCase = (str: string) =>
+ str
+ // Change capital letters into "-{lowercase letter}"
+ .replace(/([A-Z])/g, (capitalLetter, placeholderVar, index) => {
+ // If capital letter is not first character, precede with '-':
+ return (index > 0 ? "-" : "") + capitalLetter.toLowerCase()
+ })