Skip to content
Draft
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ memory-bank/
# Terraform providers
.terraform
*.tfstate
tsconfig.tsbuildinfo
158 changes: 158 additions & 0 deletions sites/partners/__tests__/pages/listings/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ import {
Jurisdiction,
} from "@bloom-housing/shared-helpers/src/types/backend-swagger"

jest.mock("@bloom-housing/shared-helpers", () => {
const actual = jest.requireActual("@bloom-housing/shared-helpers")
return {
...actual,
tIfExists: jest.fn(actual.tIfExists),
}
})

const { tIfExists } = require("@bloom-housing/shared-helpers")
const actual = jest.requireActual("@bloom-housing/shared-helpers")

//Mock the jszip package used for Export
const mockFile = jest.fn()
let mockFolder: jest.Mock
Expand Down Expand Up @@ -86,6 +97,8 @@ beforeAll(() => {
afterEach(() => {
server.resetHandlers()
window.sessionStorage.clear()
tIfExists.mockReset()
tIfExists.mockImplementation(actual.tIfExists)
})

afterAll(() => server.close())
Expand Down Expand Up @@ -782,4 +795,149 @@ describe("listings", () => {
})
})
})

describe("other portals banner", () => {
const multipleJurisdictionHandlers = [
rest.get("http://localhost:3100/listings", (_req, res, ctx) => {
return res(ctx.json({ items: [listing], meta: { totalItems: 1, totalPages: 1 } }))
}),
rest.get("http://localhost/api/adapter/listings", (_req, res, ctx) => {
return res(ctx.json({ items: [listing], meta: { totalItems: 1, totalPages: 1 } }))
}),
rest.get("http://localhost/api/adapter/user", (_req, res, ctx) => {
return res(
ctx.json({
id: "user1",
userRoles: { id: "user1", isAdmin: true, isPartner: false },
jurisdictions: [
{
id: "id1",
name: "JurisdictionA",
featureFlags: [],
} as Jurisdiction,
{
id: "id2",
name: "JurisdictionB",
featureFlags: [],
} as Jurisdiction,
],
})
)
}),
rest.post("http://localhost:3100/auth/token", (_req, res, ctx) => {
return res(ctx.json(""))
}),
]

it("should not show the other portals banner when translation key does not exist", async () => {
window.URL.createObjectURL = jest.fn()
document.cookie = "access-token-available=True"
tIfExists.mockReturnValue(null)
server.use(...multipleJurisdictionHandlers)

render(<ListingsList />)

const addListingButton = await screen.findByRole("button", { name: "Add listing" })
await userEvent.click(addListingButton)

expect(
screen.getByRole("heading", { level: 1, name: "Select jurisdiction" })
).toBeInTheDocument()
expect(screen.queryByTestId("other-portals-banner")).not.toBeInTheDocument()
})

it("should show the other portals banner with title and links when translation keys exist", async () => {
window.URL.createObjectURL = jest.fn()
document.cookie = "access-token-available=True"
tIfExists.mockImplementation((key: string) => {
const translations: Record<string, string> = {
"listings.otherPortals.title":
"To add listings in other jurisdictions, use one of these portals:",
"listings.otherPortals.portal1.name": "Other portal 1",
"listings.otherPortals.portal1.url": "https://portal1.example.com",
"listings.otherPortals.portal2.name": "Other portal 2",
"listings.otherPortals.portal2.url": "https://portal2.example.com",
}
return translations[key] ?? actual.tIfExists(key)
})
server.use(...multipleJurisdictionHandlers)

render(<ListingsList />)

const addListingButton = await screen.findByRole("button", { name: "Add listing" })
await userEvent.click(addListingButton)

expect(screen.getByTestId("other-portals-banner")).toBeInTheDocument()
expect(
screen.getByText("To add listings in other jurisdictions, use one of these portals:")
).toBeInTheDocument()
expect(screen.getByRole("link", { name: "Other portal 1" })).toHaveAttribute(
"href",
"https://portal1.example.com"
)
expect(screen.getByRole("link", { name: "Other portal 2" })).toHaveAttribute(
"href",
"https://portal2.example.com"
)
})

it("should not show the other portals banner when user has only one jurisdiction", async () => {
window.URL.createObjectURL = jest.fn()
document.cookie = "access-token-available=True"
tIfExists.mockImplementation((key: string) => {
const translations: Record<string, string> = {
"listings.otherPortals.title":
"To add listings in other jurisdictions, use one of these portals:",
"listings.otherPortals.portal1.name": "Other portal 1",
"listings.otherPortals.portal1.url": "https://portal1.example.com",
"listings.otherPortals.portal2.name": "Other portal 2",
"listings.otherPortals.portal2.url": "https://portal2.example.com",
}
return translations[key] ?? actual.tIfExists(key)
})
server.use(
rest.get("http://localhost:3100/listings", (_req, res, ctx) => {
return res(ctx.json({ items: [listing], meta: { totalItems: 1, totalPages: 1 } }))
}),
rest.get("http://localhost/api/adapter/listings", (_req, res, ctx) => {
return res(ctx.json({ items: [listing], meta: { totalItems: 1, totalPages: 1 } }))
}),
rest.get("http://localhost/api/adapter/user", (_req, res, ctx) => {
return res(
ctx.json({
id: "user1",
userRoles: { id: "user1", isAdmin: true, isPartner: false },
jurisdictions: [
{
id: "id1",
name: "JurisdictionA",
featureFlags: [
{
id: "id_1",
name: FeatureFlagEnum.enableNonRegulatedListings,
active: true,
},
],
} as Jurisdiction,
],
})
)
}),
rest.post("http://localhost:3100/auth/token", (_req, res, ctx) => {
return res(ctx.json(""))
})
)

render(<ListingsList />)

const addListingButton = await screen.findByRole("button", { name: "Add listing" })
await userEvent.click(addListingButton)

// When user has only one jurisdiction, the modal shows "Select Listing Type" not "Select jurisdiction"
expect(
screen.getByRole("heading", { level: 1, name: "Select Listing Type" })
).toBeInTheDocument()
expect(screen.queryByTestId("other-portals-banner")).not.toBeInTheDocument()
})
})
})
5 changes: 5 additions & 0 deletions sites/partners/page_content/locales/general.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,11 @@
"listings.maxAnnualIncome": "Maximum annual income",
"listings.newListing": "New listing",
"listings.nonRegulated": "Non-regulated",
"listings.otherPortals.portal1.name": "Other portal 1",
"listings.otherPortals.portal1.url": "https://www.example.com",
"listings.otherPortals.portal2.name": "Other portal 2",
"listings.otherPortals.portal2.url": "https://www.example.com",
"listings.otherPortals.title": "To add listings in other jurisdictions, use one of these portals:",
"listings.pdfHelperText": "Select PDF file",
"listings.petPolicyQuestion": "What kinds of pets do you allow?",
"listings.pickupAddress": "Pickup address",
Expand Down
10 changes: 10 additions & 0 deletions sites/partners/src/pages/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.other-portals-banner {
border: var(--seeds-border-2) solid var(--seeds-color-alert);
padding: var(--seeds-s4);

ul {
list-style-type: disc;
margin-block-start: var(--seeds-s2);
padding-left: var(--seeds-s5);
}
}
37 changes: 34 additions & 3 deletions sites/partners/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { useRouter } from "next/router"
import { useForm } from "react-hook-form"
import dayjs from "dayjs"
import { ColDef, ColGroupDef } from "ag-grid-community"
import { Button, Dialog, Grid, Icon } from "@bloom-housing/ui-seeds"
import { Button, Dialog, Grid, Icon, Link } from "@bloom-housing/ui-seeds"
import { t, Select, SelectOption, Field } from "@bloom-housing/ui-components"
import { AgTable, useAgTable } from "@bloom-housing/ui-components/ag-table"
import { AuthContext, Form } from "@bloom-housing/shared-helpers"
import { AuthContext, Form, tIfExists } from "@bloom-housing/shared-helpers"
import {
EnumListingListingType,
FeatureFlagEnum,
Expand All @@ -17,6 +17,7 @@ import {
import { useListingExport, useListingsData } from "../lib/hooks"
import Layout from "../layouts"
import { NavigationHeader } from "../components/shared/NavigationHeader"
import styles from "./index.module.scss"

class formatLinkCell {
link: HTMLAnchorElement
Expand Down Expand Up @@ -120,6 +121,12 @@ export default function ListingsList() {
const defaultJurisdiction =
profile?.jurisdictions?.length === 1 ? profile.jurisdictions[0].id : null

const otherPortalsTitle = tIfExists("listings.otherPortals.title")
const otherPortalsPortal1Name = tIfExists("listings.otherPortals.portal1.name")
const otherPortalsPortal1Url = tIfExists("listings.otherPortals.portal1.url")
const otherPortalsPortal2Name = tIfExists("listings.otherPortals.portal2.name")
const otherPortalsPortal2Url = tIfExists("listings.otherPortals.portal2.url")

Comment thread
emilyjablonski marked this conversation as resolved.
const jurisdictions = profile?.jurisdictions || []

const jurisdictionOptions: SelectOption[] = [
Expand Down Expand Up @@ -446,9 +453,33 @@ export default function ListingsList() {
<Dialog.Content id="listing-select-dialog-content">
{t("listings.selectJurisdictionContent")}
<Grid>
<Grid.Row className={"seeds-m-bs-4"}>
<Grid.Cell>
{otherPortalsTitle && !defaultJurisdiction && (
<div
className={styles["other-portals-banner"]}
data-testid={"other-portals-banner"}
>
<p>{otherPortalsTitle}</p>
<ul>
{otherPortalsPortal1Name && otherPortalsPortal1Url && (
<li>
<Link href={otherPortalsPortal1Url}>{otherPortalsPortal1Name}</Link>
</li>
)}
{otherPortalsPortal2Name && otherPortalsPortal2Url && (
<li>
<Link href={otherPortalsPortal2Url}>{otherPortalsPortal2Name}</Link>
</li>
)}
</ul>
</div>
)}
</Grid.Cell>
</Grid.Row>
<Grid.Row columns={3}>
<Grid.Cell className={"seeds-grid-span-2"}>
<div className={`${defaultJurisdiction ? "hidden" : ""} seeds-m-bs-4`}>
<div className={`${defaultJurisdiction ? "hidden" : ""}`}>
<Select
id={"jurisdiction"}
defaultValue={defaultJurisdiction}
Expand Down
Loading