Skip to content

Commit

Permalink
fix: community type disclaimer frontend (#4496)
Browse files Browse the repository at this point in the history
* fix: add community type disclaimer to public site

* fix: add community type disclaimer to partners

* fix: add partners tests

* fix: form type issue

* fix: failing build

* fix: allow reserved community disclaimer only when community type selected

* fix: tests

* fix: be tests

* fix: add translations with tests
  • Loading branch information
KrissDrawing authored Dec 5, 2024
1 parent e415120 commit 2769691
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 53 deletions.
9 changes: 9 additions & 0 deletions api/src/dtos/listings/listing.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
IsUrl,
MaxLength,
Validate,
ValidateIf,
ValidateNested,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
Expand Down Expand Up @@ -622,11 +623,19 @@ class Listing extends AbstractDTO {

@Expose()
@ApiPropertyOptional()
@ValidateIf((o) => o.includeCommunityDisclaimer, {
groups: [ValidationsGroupsEnum.default],
})
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@IsString({ groups: [ValidationsGroupsEnum.default] })
communityDisclaimerTitle?: string;

@Expose()
@ApiPropertyOptional()
@ValidateIf((o) => o.includeCommunityDisclaimer, {
groups: [ValidationsGroupsEnum.default],
})
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@IsString({ groups: [ValidationsGroupsEnum.default] })
communityDisclaimerDescription?: string;

Expand Down
7 changes: 7 additions & 0 deletions api/src/services/translation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ export class TranslationService {
});
}

if (listing.includeCommunityDisclaimer) {
pathsToFilter[`communityDisclaimerTitle`] =
listing.communityDisclaimerTitle;
pathsToFilter[`communityDisclaimerDescription`] =
listing.communityDisclaimerDescription;
}

const persistedTranslationsFromDB = await this.getPersistedTranslatedValues(
listing,
language,
Expand Down
14 changes: 9 additions & 5 deletions api/test/integration/listing.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ describe('Listing Controller Tests', () => {
label: 'example asset label',
};

const shouldIncludeCommunityDisclaimer = Math.random() >= 0.5;

return {
id: listingId ?? undefined,
assets: [exampleAsset],
Expand Down Expand Up @@ -344,11 +346,13 @@ describe('Listing Controller Tests', () => {
phone: false,
internet: true,
},
includeCommunityDisclaimer: Math.random() >= 0.5,
communityDisclaimerTitle:
Math.random() >= 0.5 ? 'example title' : undefined,
communityDisclaimerDescription:
Math.random() >= 0.5 ? 'example description' : undefined,
includeCommunityDisclaimer: shouldIncludeCommunityDisclaimer,
communityDisclaimerTitle: shouldIncludeCommunityDisclaimer
? 'example title'
: undefined,
communityDisclaimerDescription: shouldIncludeCommunityDisclaimer
? 'example description'
: undefined,
homeType: 'apartment',
};
};
Expand Down
12 changes: 12 additions & 0 deletions api/test/unit/services/translation.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ const mockListing = (): Listing => {
},
},
],
includeCommunityDisclaimer: true,
communityDisclaimerTitle: 'untranslated community disclaimer title',
communityDisclaimerDescription:
'untranslated community disclaimer description',
};
};

Expand Down Expand Up @@ -133,6 +137,8 @@ const translatedStrings = [
'translated multiselect description',
'translated multiselect subtext',
'translated multiselect opt out text',
'translated community disclaimer title',
'translated community disclaimer description',
];

describe('Testing translations service', () => {
Expand Down Expand Up @@ -395,4 +401,10 @@ const validateTranslatedFields = (listing: Listing) => {
expect(
listing.listingMultiselectQuestions[0].multiselectQuestions.optOutText,
).toEqual('translated multiselect opt out text');
expect(listing.communityDisclaimerTitle).toEqual(
'translated community disclaimer title',
);
expect(listing.communityDisclaimerDescription).toEqual(
'translated community disclaimer description',
);
};
10 changes: 10 additions & 0 deletions sites/partners/cypress/e2e/default/03-listing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ describe("Listing Management Tests", () => {
cy.contains("Listing Data")
// Try to publish a listing and should show errors for appropriate fields
cy.getByID("listingEditButton").contains("Edit").click()
cy.getByID("reservedCommunityTypes.id").select(1)
cy.getByID("includeCommunityDisclaimerYes").check()
cy.getByID("publishButton").contains("Publish").click()
cy.getByID("publishButtonConfirm").contains("Publish").click()
cy.contains("Please resolve any errors before saving or publishing your listing.")
Expand All @@ -42,6 +44,8 @@ describe("Listing Management Tests", () => {
expect($alertButtons[1]).to.have.id("addUnitsButton")
})
cy.getByID("units-error").contains("This field is required")
cy.getByID("communityDisclaimerTitle-error").contains("Enter title")
cy.get(".textarea-error-message").contains("Enter description")
cy.getByID("applicationProcessButton").contains("Application Process").click()
cy.getByID("leasingAgentName-error").contains("This field is required")
cy.getByID("leasingAgentEmail-error").contains("This field is required")
Expand Down Expand Up @@ -158,6 +162,9 @@ describe("Listing Management Tests", () => {
cy.get(".addressPopup").contains(listing["buildingAddress.street"])
cy.getByID("reservedCommunityTypes.id").select(listing["reservedCommunityType.id"])
cy.getByID("reservedCommunityDescription").type(listing["reservedCommunityDescription"])
cy.getByID("includeCommunityDisclaimerYes").check()
cy.getByID("communityDisclaimerTitle").type(listing["communityDisclaimerTitle"])
cy.getByID("communityDisclaimerDescription").type(listing["communityDisclaimerDescription"])
cy.getByTestId("unit-types").check()
cy.getByTestId("listingAvailability.availableUnits").check()
if (listing["homeType"]) {
Expand Down Expand Up @@ -306,6 +313,9 @@ describe("Listing Management Tests", () => {
cy.getByID("latitude").should("include.text", "37.7")
cy.getByID("reservedCommunityType").contains(listing["reservedCommunityType.id"])
cy.getByID("reservedCommunityDescription").contains(listing["reservedCommunityDescription"])
cy.getByID("includeCommunityDisclaimer").contains("Yes")
cy.getByID("communityDisclaimerTitle").contains(listing["communityDisclaimerTitle"])
cy.getByID("communityDisclaimerDescription").contains(listing["communityDisclaimerDescription"])
if (listing["homeType"]) {
cy.getByID("homeType").contains(listing["homeType"])
}
Expand Down
2 changes: 2 additions & 0 deletions sites/partners/cypress/fixtures/listing.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"yearBuilt": "2021",
"reservedCommunityType.id": "Seniors",
"reservedCommunityDescription": "Basic Test Description",
"communityDisclaimerTitle": "Basic Test Title",
"communityDisclaimerDescription": "Basic Test Description",
"homeType": "Apartment",
"number": "2",
"unitType.id": "One Bedroom",
Expand Down
7 changes: 7 additions & 0 deletions sites/partners/page_content/locale_overrides/general.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
"listings.addPreferences": "Add Preferences",
"listings.additionalApplicationSubmissionNotes": "Additional Application Submission Notes",
"listings.amiOverrideTitle": "Override for household size of %{householdSize}",
"listings.appearsInListing": "Appears in listing",
"listings.applicationDropOffQuestion": "Can applications be dropped off?",
"listings.applicationDueTime": "Application Due Time",
"listings.applicationPickupQuestion": "Can applications be picked up?",
Expand Down Expand Up @@ -316,7 +317,11 @@
"listings.referralContactPhone": "Referral Contact Phone",
"listings.referralSummary": "Referral Summary",
"listings.requiredToPublish": "Required to publish",
"listings.requiredToPublishAppearsAsFirstPage": "Required to publish, appears as first page of application",
"listings.reservedCommunityDescription": "Reserved Community Description",
"listings.reservedCommunityDisclaimer": "Reserved Community Disclaimer",
"listings.reservedCommunityDisclaimerTitle": "Reserved Community Disclaimer Title",
"listings.includeCommunityDisclaimer": "Do you want to include a community type disclaimer as the first page of the application?",
"listings.reviewOrderQuestion": "How is the application review order determined?",
"listings.sections.addOpenHouse": "Add Open House",
"listings.sections.additionalDetails": "Additional Details",
Expand Down Expand Up @@ -475,6 +480,8 @@
"t.end": "End",
"t.endTime": "End Time",
"t.enterAmount": "Enter amount",
"t.enterDescription": "Enter description",
"t.enterTitle": "Enter title",
"t.error": "Error",
"t.errorOccurred": "An error has occurred.",
"t.exit": "Exit",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import SectionWithGrid from "../../../shared/SectionWithGrid"
const DetailCommunityType = () => {
const listing = useContext(ListingContext)

const includeCommunityDisclaimer = listing.includeCommunityDisclaimer

return (
<SectionWithGrid heading={t("listings.sections.communityType")} inset>
<Grid.Row>
Expand All @@ -27,6 +29,33 @@ const DetailCommunityType = () => {
{getDetailFieldString(listing.reservedCommunityDescription)}
</FieldValue>
</Grid.Row>

<Grid.Row>
<FieldValue
id="includeCommunityDisclaimer"
label={t("listings.includeCommunityDisclaimer")}
>
{includeCommunityDisclaimer ? t("t.yes") : t("t.no")}
</FieldValue>
</Grid.Row>
{includeCommunityDisclaimer && (
<>
<Grid.Row>
<FieldValue
id="communityDisclaimerTitle"
label={t("listings.reservedCommunityDisclaimerTitle")}
>
{getDetailFieldString(listing.communityDisclaimerTitle)}
</FieldValue>
<FieldValue
id="communityDisclaimerDescription"
label={t("listings.reservedCommunityDisclaimer")}
>
{getDetailFieldString(listing.communityDisclaimerDescription)}
</FieldValue>
</Grid.Row>
</>
)}
</SectionWithGrid>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React, { useEffect, useState } from "react"
import { useFormContext } from "react-hook-form"
import { t, Select, Textarea } from "@bloom-housing/ui-components"
import { t, Select, Textarea, FieldGroup, Field } from "@bloom-housing/ui-components"
import { FieldValue, Grid } from "@bloom-housing/ui-seeds"
import { ReservedCommunityType } from "@bloom-housing/shared-helpers/src/types/backend-swagger"
import {
ReservedCommunityType,
YesNoEnum,
} from "@bloom-housing/shared-helpers/src/types/backend-swagger"
import { useReservedCommunityTypeList } from "../../../../lib/hooks"
import { arrayToFormOptions } from "../../../../lib/helpers"
import { FormListing } from "../../../../lib/listings/formTypes"
Expand All @@ -16,7 +19,7 @@ const CommunityType = ({ listing }: CommunityTypeProps) => {
const formMethods = useFormContext()

// eslint-disable-next-line @typescript-eslint/unbound-method
const { register, setValue, watch } = formMethods
const { register, setValue, watch, errors } = formMethods

const reservedCommunityType = watch("reservedCommunityTypes.id")

Expand Down Expand Up @@ -44,6 +47,19 @@ const CommunityType = ({ listing }: CommunityTypeProps) => {
}
}, [reservedCommunityType, listing?.reservedCommunityTypes?.id])

useEffect(() => {
if (
listing &&
watch("includeCommunityDisclaimerQuestion") === null &&
listing?.includeCommunityDisclaimer !== null
) {
setValue(
"includeCommunityDisclaimerQuestion",
listing?.includeCommunityDisclaimer ? YesNoEnum.yes : YesNoEnum.no
)
}
}, [setValue, listing?.includeCommunityDisclaimer, watch, listing])

return (
<>
<hr className="spacer-section-above spacer-section" />
Expand Down Expand Up @@ -79,9 +95,68 @@ const CommunityType = ({ listing }: CommunityTypeProps) => {
id={"reservedCommunityDescription"}
fullWidth={true}
register={register}
note={t("listings.appearsInListing")}
/>
</Grid.Cell>
</Grid.Row>

<Grid.Row columns={1}>
<FieldValue label={t("listings.includeCommunityDisclaimer")}>
<FieldGroup
name="includeCommunityDisclaimerQuestion"
type="radio"
register={register}
fields={[
{
label: t("t.yes"),
value: YesNoEnum.yes,
id: "includeCommunityDisclaimerYes",
disabled: !currentCommunityType,
},
{
label: t("t.no"),
value: YesNoEnum.no,
id: "includeCommunityDisclaimerNo",
disabled: !currentCommunityType,
},
]}
/>
</FieldValue>
</Grid.Row>

{watch("includeCommunityDisclaimerQuestion") === YesNoEnum.yes && currentCommunityType && (
<>
<Grid.Row columns={3}>
<Grid.Cell className="seeds-grid-span-2">
<Field
label={t("listings.reservedCommunityDisclaimerTitle")}
name="communityDisclaimerTitle"
id="communityDisclaimerTitle"
register={register}
subNote={t("listings.requiredToPublishAppearsAsFirstPage")}
error={errors.communityDisclaimerTitle}
placeholder={t("t.enterTitle")}
errorMessage={t("t.enterTitle")}
/>
</Grid.Cell>
</Grid.Row>

<Grid.Row columns={3}>
<Grid.Cell className="seeds-grid-span-2">
<Textarea
label={t("listings.reservedCommunityDisclaimer")}
name="communityDisclaimerDescription"
id="communityDisclaimerDescription"
fullWidth={true}
register={register}
note={t("listings.requiredToPublishAppearsAsFirstPage")}
placeholder={t("t.enterDescription")}
errorMessage={errors.communityDisclaimerDescription && t("t.enterDescription")}
/>
</Grid.Cell>
</Grid.Row>
</>
)}
</SectionWithGrid>
</>
)
Expand Down
15 changes: 13 additions & 2 deletions sites/partners/src/lib/listings/AdditionalMetadataFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { listingFeatures, listingUtilities } from "@bloom-housing/shared-helpers"
import { ReviewOrderTypeEnum } from "@bloom-housing/shared-helpers/src/types/backend-swagger"
import {
ReviewOrderTypeEnum,
YesNoEnum,
} from "@bloom-housing/shared-helpers/src/types/backend-swagger"
import Formatter from "./Formatter"

export default class AdditionalMetadataFormatter extends Formatter {
Expand Down Expand Up @@ -55,7 +58,15 @@ export default class AdditionalMetadataFormatter extends Formatter {

this.data.customMapPin = this.metadata.customMapPositionChosen
this.data.yearBuilt = this.data.yearBuilt ? Number(this.data.yearBuilt) : null
if (!this.data.reservedCommunityTypes?.id) this.data.reservedCommunityTypes = null
if (!this.data.reservedCommunityTypes?.id) {
this.data.reservedCommunityTypes = null
this.data.includeCommunityDisclaimer = null
this.data.communityDisclaimerTitle = ""
this.data.communityDisclaimerDescription = ""
} else if (this.data.includeCommunityDisclaimerQuestion === YesNoEnum.no) {
this.data.communityDisclaimerTitle = ""
this.data.communityDisclaimerDescription = ""
}
this.data.reviewOrderType =
this.data.reviewOrderQuestion === "reviewOrderLottery"
? ReviewOrderTypeEnum.lottery
Expand Down
5 changes: 5 additions & 0 deletions sites/partners/src/lib/listings/BooleansFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,10 @@ export default class BooleansFormatter extends Formatter {
when: this.data.lotteryOptInQuestion === YesNoEnum.yes,
falseCase: () => (this.data.lotteryOptInQuestion === YesNoEnum.no ? false : null),
})
this.processBoolean("includeCommunityDisclaimer", {
when: this.data.includeCommunityDisclaimerQuestion === YesNoEnum.yes,
falseCase: () =>
this.data.includeCommunityDisclaimerQuestion === YesNoEnum.no ? false : null,
})
}
}
3 changes: 3 additions & 0 deletions sites/partners/src/lib/listings/formTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export type FormListing = Omit<Listing, "countyCode"> & {
paperApplicationChoice?: YesNoEnum
referralOpportunityChoice?: YesNoEnum
criteriaAttachType?: string
includeCommunityDisclaimerQuestion?: YesNoEnum
communityDisclaimerTitle?: string
communityDisclaimerDescription?: string
lotteryDate?: {
month: string
day: string
Expand Down
Loading

0 comments on commit 2769691

Please sign in to comment.