@bloom-housing/ui-components 6.0.0 → 6.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +214 -9
  2. package/package.json +3 -5
  3. package/src/actions/Button.tsx +1 -0
  4. package/src/actions/LinkButton.tsx +1 -0
  5. package/src/blocks/FormCard.scss +1 -0
  6. package/src/blocks/HousingCounselor.tsx +8 -3
  7. package/src/blocks/ImageCard.tsx +24 -13
  8. package/src/blocks/MediaCard.docs.mdx +37 -0
  9. package/src/blocks/MediaCard.scss +10 -11
  10. package/src/blocks/MediaCard.tsx +4 -4
  11. package/src/blocks/StandardCard.tsx +1 -1
  12. package/src/blocks/StatusItem.tsx +17 -6
  13. package/src/forms/DOBField.tsx +20 -8
  14. package/src/forms/DateField.tsx +16 -7
  15. package/src/forms/Dropzone.scss +7 -0
  16. package/src/forms/Dropzone.tsx +18 -5
  17. package/src/forms/Field.tsx +5 -0
  18. package/src/forms/FieldGroup.tsx +14 -3
  19. package/src/forms/HouseholdMemberForm.tsx +4 -1
  20. package/src/forms/HouseholdSizeField.tsx +16 -6
  21. package/src/forms/TimeField.tsx +15 -6
  22. package/src/global/custom_counter.scss +1 -1
  23. package/src/global/forms.scss +38 -5
  24. package/src/global/headers.scss +1 -1
  25. package/src/global/markdown.scss +2 -2
  26. package/src/headers/Hero.tsx +8 -1
  27. package/src/headers/PageHeader.scss +1 -1
  28. package/src/headers/PageHeader.tsx +5 -1
  29. package/src/headers/SiteHeader.tsx +11 -4
  30. package/src/helpers/formOptions.tsx +4 -1
  31. package/src/helpers/formatYesNoLabel.ts +8 -6
  32. package/src/locales/es.json +1 -1
  33. package/src/locales/general.json +9 -4
  34. package/src/locales/tl.json +1 -1
  35. package/src/locales/vi.json +1 -1
  36. package/src/locales/zh.json +1 -1
  37. package/src/navigation/Breadcrumbs.tsx +1 -1
  38. package/src/navigation/FooterNav.tsx +5 -1
  39. package/src/navigation/LanguageNav.tsx +1 -1
  40. package/src/navigation/ProgressNav.docs.mdx +47 -0
  41. package/src/navigation/ProgressNav.scss +101 -56
  42. package/src/navigation/ProgressNav.tsx +45 -15
  43. package/src/navigation/TabNav.scss +1 -1
  44. package/src/navigation/TabNav.tsx +1 -1
  45. package/src/notifications/AlertBox.docs.mdx +41 -0
  46. package/src/notifications/AlertBox.scss +78 -41
  47. package/src/notifications/AlertBox.tsx +20 -14
  48. package/src/notifications/SiteAlert.tsx +3 -0
  49. package/src/notifications/StatusMessage.tsx +8 -2
  50. package/src/notifications/alertTypes.ts +1 -0
  51. package/src/overlays/Modal.scss +3 -1
  52. package/src/page_components/ApplicationTimeline.scss +6 -6
  53. package/src/page_components/ApplicationTimeline.tsx +17 -7
  54. package/src/page_components/forgot-password/FormForgotPassword.tsx +1 -1
  55. package/src/page_components/listing/AdditionalFees.tsx +1 -1
  56. package/src/page_components/listing/ListingCard.scss +4 -0
  57. package/src/page_components/listing/ListingCard.tsx +18 -3
  58. package/src/page_components/listing/listing_sidebar/Contact.tsx +2 -2
  59. package/src/page_components/listing/listing_sidebar/GetApplication.tsx +31 -16
  60. package/src/page_components/listing/listing_sidebar/ListingUpdated.tsx +5 -1
  61. package/src/page_components/listing/listing_sidebar/OrDivider.tsx +4 -2
  62. package/src/page_components/listing/listing_sidebar/ReferralApplication.tsx +7 -4
  63. package/src/page_components/listing/listing_sidebar/events/DownloadLotteryResults.tsx +6 -1
  64. package/src/page_components/sign-in/FormSignIn.tsx +1 -1
  65. package/src/page_components/sign-in/FormSignInErrorBox.tsx +1 -1
  66. package/src/sections/InfoCardGrid.scss +1 -1
  67. package/src/sections/InfoCardGrid.tsx +4 -1
  68. package/src/sections/ListSection.tsx +1 -1
  69. package/src/tables/AgTable.tsx +10 -4
  70. package/src/tables/StandardTable.tsx +19 -7
  71. package/src/text/Tag.scss +7 -0
  72. package/src/text/Tag.tsx +2 -0
@@ -1,86 +1,123 @@
1
1
  .alert-box {
2
- @apply relative;
3
- @apply py-3;
4
- @apply px-4;
5
- @apply leading-snug;
6
- @apply flex;
7
- @apply items-center;
2
+ --vertical-padding: var(--bloom-s3);
3
+ --horizontal-padding: var(--bloom-s4);
4
+ --background-color: var(--bloom-color-primary-light);
5
+ --alert-background-color: var(--bloom-color-alert-light);
6
+ --alert-invert-background-color: var(--bloom-color-alert);
7
+ --notice-background-color: var(--bloom-color-primary-light);
8
+ --notice-invert-background-color: var(--bloom-color-primary);
9
+ --success-background-color: var(--bloom-color-success-light);
10
+ --success-invert-background-color: var(--bloom-color-success);
11
+ --warn-background-color: var(--bloom-color-warn-light);
12
+ --warn-invert-background-color: var(--bloom-color-warn);
13
+ --text-color: var(--bloom-color-gray-900);
14
+ --close-icon-color: var(--bloom-color-gray-900);
15
+ --font-weight: 500;
16
+ --max-width: var(--bloom-width-5xl);
17
+ --line-height: 1.375;
18
+
19
+ padding: var(--vertical-padding) var(--horizontal-padding);
20
+ background-color: var(--background-color);
21
+ color: var(--text-color);
22
+ font-weight: var(--font-weight);
23
+ position: relative;
24
+ display: flex;
25
+ align-items: center;
26
+ line-height: var(--line-height);
8
27
 
9
28
  .alert-box_inner {
10
- @apply m-auto;
11
- @apply max-w-5xl;
12
- @apply px-4;
13
- @apply flex-1;
14
- @apply flex;
15
- @apply items-center;
29
+ margin: auto;
30
+ max-width: var(--max-width);
31
+ padding-right: var(--horizontal-padding);
32
+ padding-left: var(--horizontal-padding);
33
+ flex: 1;
34
+ display: flex;
35
+ align-items: center;
16
36
  }
17
37
 
18
38
  &.narrow {
19
- padding-top: 0.35rem;
20
- padding-bottom: 0.35rem;
39
+ padding-top: var(--bloom-s1_5);
40
+ padding-bottom: var(--bloom-s1_5);
21
41
  }
22
42
 
23
- // Inverts color scheme of alert
24
43
  &.invert {
25
- @apply text-white;
26
-
27
- .close {
28
- @apply text-white;
44
+ color: var(--bloom-color-white);
45
+ a {
46
+ color: var(--bloom-color-white);
47
+ text-decoration: underline;
29
48
  }
30
49
  }
31
50
 
32
51
  &.alert {
33
- @apply bg-alert-light;
52
+ background-color: var(--alert-background-color);
34
53
 
35
54
  &.invert {
36
- @apply bg-alert;
55
+ background-color: var(--alert-invert-background-color);
37
56
  }
38
57
  }
39
58
 
40
59
  &.primary {
41
- @apply bg-primary-light;
60
+ background-color: var(--notice-background-color);
42
61
 
43
62
  &.invert {
44
- @apply bg-primary;
63
+ background-color: var(--notice-invert-background-color);
45
64
  }
46
65
  }
47
66
 
48
67
  &.success {
49
- @apply bg-success-light;
68
+ background-color: var(--success-background-color);
50
69
 
51
70
  &.invert {
52
- @apply bg-success;
71
+ background-color: var(--success-invert-background-color);
53
72
  }
54
73
  }
55
74
 
56
75
  &.warn {
57
- @apply bg-warn-light;
58
-
59
- &.invert {
60
- @apply bg-warn;
61
- }
76
+ background-color: var(--warn-background-color);
62
77
  }
63
78
  }
64
79
 
65
80
  .alert-box__head {
66
- @apply flex justify-between;
67
- @apply w-full;
81
+ display: flex;
82
+ justify-content: space-between;
83
+ width: 100%;
68
84
  }
69
85
 
70
86
  .alert-box__title {
71
- @apply flex justify-between;
87
+ display: flex;
88
+ justify-content: space-between;
89
+ align-items: center;
72
90
  }
73
91
 
74
92
  .alert-box__body {
75
- @apply ml-2;
76
- @apply font-semibold;
77
- @apply text-tiny;
93
+ font-weight: var(--font-weight);
94
+ font-size: var(--bloom-font-size-tiny);
78
95
  }
79
96
 
80
97
  .alert-box__close {
81
- @apply text-3xl;
82
- right: 1rem;
83
- @apply ml-3;
84
- @apply p-0;
85
- line-height: 1rem;
98
+ font-size: var(--bloom-font-size-3xl);
99
+ right: var(--horizontal-padding);
100
+ margin-left: var(--bloom-s3);
101
+ padding: 0;
102
+ line-height: var(--bloom-s4);
103
+ color: var(--close-icon-color);
104
+ }
105
+
106
+ .alert-box__icon {
107
+ margin-right: var(--bloom-s3);
108
+ }
109
+
110
+ .alert-box__sticky {
111
+ position: sticky;
112
+ top: var(--bloom-s3);
113
+ margin-top: var(--bloom-s3);
114
+ margin-right: var(--bloom-s3);
115
+ z-index: 10;
116
+ margin-left: auto;
117
+ width: fit-content;
118
+ }
119
+
120
+ .alert-box__sticky + * {
121
+ --alert-box-height: -3.6rem;
122
+ margin-top: var(--alert-box-height);
86
123
  }
@@ -6,20 +6,23 @@ import { colorClasses } from "./alertTypes"
6
6
  import "./AlertBox.scss"
7
7
 
8
8
  export interface AlertBoxProps {
9
- type?: AlertTypes
10
- closeable?: boolean
11
- onClose?: () => void
9
+ boundToLayoutWidth?: boolean
12
10
  children: ReactNode
13
- inverted?: boolean
14
11
  className?: string
15
- boundToLayoutWidth?: boolean
12
+ closeable?: boolean
13
+ customIcon?: IconTypes
14
+ inverted?: boolean
16
15
  narrow?: boolean
16
+ onClose?: () => void
17
+ sticky?: boolean
18
+ type?: AlertTypes
17
19
  }
18
20
 
19
21
  const icons: { [k in AlertTypes]: IconTypes } = {
20
22
  alert: "warning",
21
23
  notice: "info",
22
24
  success: "check",
25
+ warn: "warning",
23
26
  }
24
27
 
25
28
  const AlertBox = (props: AlertBoxProps) => {
@@ -28,11 +31,12 @@ const AlertBox = (props: AlertBoxProps) => {
28
31
 
29
32
  const classNames = [
30
33
  "alert-box",
31
- colorClasses[props.type || "alert"],
34
+ colorClasses[props.type || ""],
32
35
  ...(props.inverted ? ["invert"] : []),
33
36
  ...(props.className ? [props.className] : []),
34
37
  ...(props.boundToLayoutWidth ? [] : ["fullWidth"]),
35
38
  ...(props.narrow ? ["narrow"] : []),
39
+ ...(props.sticky ? ["alert-box__sticky"] : []),
36
40
  ].join(" ")
37
41
 
38
42
  if (onClose) closeable = true
@@ -47,14 +51,16 @@ const AlertBox = (props: AlertBoxProps) => {
47
51
  <>
48
52
  <div className="alert-box__head">
49
53
  <div className="alert-box__title">
50
- <span className="alert-box__icon">
51
- <Icon
52
- size="medium"
53
- symbol={icons[props.type || "alert"]}
54
- fill={props.inverted ? IconFillColors.white : undefined}
55
- ariaHidden={true}
56
- />
57
- </span>
54
+ {(props.type || props.customIcon) && (
55
+ <span className="alert-box__icon">
56
+ <Icon
57
+ size="medium"
58
+ symbol={props.type ? icons[props.type] : props.customIcon ?? "warning"}
59
+ fill={props.inverted ? IconFillColors.white : undefined}
60
+ ariaHidden={true}
61
+ />
62
+ </span>
63
+ )}
58
64
  <span className="alert-box__body">
59
65
  {typeof props.children === "string" ? <p>{props.children}</p> : props.children}
60
66
  </span>
@@ -11,6 +11,7 @@ type SiteAlertProps = {
11
11
  type: AlertTypes
12
12
  message: string
13
13
  }
14
+ sticky?: boolean
14
15
  }
15
16
 
16
17
  export const setSiteAlertMessage = (message: string, type: AlertTypes) => {
@@ -30,6 +31,7 @@ export const SiteAlert = ({
30
31
  type = "alert",
31
32
  className,
32
33
  alertMessage,
34
+ sticky,
33
35
  }: SiteAlertProps) => {
34
36
  const [open, setOpen] = useState(false)
35
37
  const [message, setMessage] = useState("")
@@ -75,6 +77,7 @@ export const SiteAlert = ({
75
77
  onClose={dismissable ? () => setOpen(false) : undefined}
76
78
  className={className}
77
79
  type={alertMessage?.type ?? type}
80
+ sticky={sticky}
78
81
  >
79
82
  {message}
80
83
  </AlertBox>
@@ -8,6 +8,10 @@ import "./StatusMessage.scss"
8
8
  export interface StatusMessagesProps {
9
9
  lastTimestamp?: string
10
10
  children?: React.ReactNode
11
+ strings?: {
12
+ lastUpdated?: string
13
+ statusHistory?: string
14
+ }
11
15
  }
12
16
 
13
17
  export const StatusMessages = (props: StatusMessagesProps) => {
@@ -17,7 +21,7 @@ export const StatusMessages = (props: StatusMessagesProps) => {
17
21
  {props.lastTimestamp && (
18
22
  <li className="status-message">
19
23
  <div className="status-message__note text-center">
20
- {t("t.lastUpdated")}: {props.lastTimestamp}
24
+ {props.strings?.lastUpdated ?? t("t.lastUpdated")}: {props.lastTimestamp}
21
25
  </div>
22
26
  </li>
23
27
  )}
@@ -26,7 +30,9 @@ export const StatusMessages = (props: StatusMessagesProps) => {
26
30
  } else {
27
31
  return (
28
32
  <>
29
- <h3 className="status-messages__title">{t("t.statusHistory")}</h3>
33
+ <h3 className="status-messages__title">
34
+ {props.strings?.statusHistory ?? t("t.statusHistory")}
35
+ </h3>
30
36
  <ul className="status-messages">{props.children}</ul>
31
37
  </>
32
38
  )
@@ -2,6 +2,7 @@ export const colorClasses = {
2
2
  alert: "alert",
3
3
  notice: "primary",
4
4
  success: "success",
5
+ warn: "warn",
5
6
  }
6
7
 
7
8
  export type AlertTypes = keyof typeof colorClasses
@@ -16,6 +16,7 @@
16
16
 
17
17
  position: relative;
18
18
  max-width: var(--max-width);
19
+ max-height: 100vh;
19
20
  margin: auto;
20
21
  border: var(--modal-border);
21
22
  background-color: var(--background-color);
@@ -33,7 +34,7 @@
33
34
 
34
35
  .modal__title {
35
36
  padding-block: var(--bloom-s6) var(--bloom-s3);
36
- padding-inline: var(--bloom-s6);
37
+ padding-inline: var(--bloom-s6) var(--bloom-s12);
37
38
  color: var(--title-color);
38
39
  font-size: var(--title-font-size);
39
40
  font-family: var(--title-font-family);
@@ -52,6 +53,7 @@
52
53
  max-height: calc(100vh - 200px);
53
54
  overflow-y: auto;
54
55
  }
56
+
55
57
  p {
56
58
  color: var(--content-font-color);
57
59
  }
@@ -4,13 +4,13 @@
4
4
  }
5
5
 
6
6
  .application-timeline {
7
- .progress-nav__item {
7
+ .progress-nav__dot-item {
8
8
  @apply pt-0;
9
9
 
10
10
  text-transform: unset;
11
11
 
12
12
  .absolute {
13
- margin-top: 0.45rem;
13
+ margin-top: 0.25rem;
14
14
  z-index: 2;
15
15
  margin-left: -0.4rem;
16
16
  }
@@ -20,17 +20,17 @@
20
20
  }
21
21
  }
22
22
 
23
- .progress-nav__item.is-active::before,
24
- .progress-nav__item.is-disabled::before {
23
+ .progress-nav__dot-item.is-active::before,
24
+ .progress-nav__dot-item.is-disabled::before {
25
25
  height: 2rem;
26
26
  width: 2rem;
27
27
  }
28
28
 
29
- .progress-nav__item.is-active::before {
29
+ .progress-nav__dot-item.is-active::before {
30
30
  @apply bg-green-700;
31
31
  }
32
32
 
33
- .progress-nav__item::after {
33
+ .progress-nav__dot-item::after {
34
34
  top: 1rem;
35
35
  }
36
36
  }
@@ -4,27 +4,37 @@ import { Icon } from "../icons/Icon"
4
4
  import { t } from "../helpers/translator"
5
5
  import "./ApplicationTimeline.scss"
6
6
 
7
- const ApplicationTimeline = () => (
7
+ export interface ApplicationTimelineProps {
8
+ strings?: {
9
+ applicationReceived?: string
10
+ applicationsClosed?: string
11
+ applicationsRanked?: string
12
+ }
13
+ }
14
+ const ApplicationTimeline = (props: ApplicationTimelineProps) => (
8
15
  <ul
9
16
  className="progress-nav application-timeline"
10
17
  aria-label="Steps of processing your application"
11
18
  >
12
- <li className="progress-nav__item is-active" aria-current="step">
19
+ <li className="progress-nav__dot-item is-active" aria-current="step">
13
20
  <span className="text-white absolute">
14
21
  <Icon symbol="check" size="base" />
15
22
  </span>
16
23
  <Markdown className="font-bold" options={{ disableParsingRawHTML: true }}>
17
- {t("application.review.confirmation.applicationReceived")}
24
+ {props.strings?.applicationReceived ??
25
+ t("application.review.confirmation.applicationReceived")}
18
26
  </Markdown>
19
27
  </li>
20
- <li className="progress-nav__item is-disabled">
28
+ <li className="progress-nav__dot-item is-disabled">
21
29
  <Markdown options={{ disableParsingRawHTML: true }}>
22
- {t("application.review.confirmation.applicationsClosed")}
30
+ {props.strings?.applicationsClosed ??
31
+ t("application.review.confirmation.applicationsClosed")}
23
32
  </Markdown>
24
33
  </li>
25
- <li className="progress-nav__item is-disabled">
34
+ <li className="progress-nav__dot-item is-disabled">
26
35
  <Markdown options={{ disableParsingRawHTML: true }}>
27
- {t("application.review.confirmation.applicationsRanked")}
36
+ {props.strings?.applicationsRanked ??
37
+ t("application.review.confirmation.applicationsRanked")}
28
38
  </Markdown>
29
39
  </li>
30
40
  </ul>
@@ -53,7 +53,7 @@ const FormForgotPassword = ({
53
53
  <FormCard>
54
54
  <div className="form-card__lead text-center border-b mx-0">
55
55
  <Icon size="2xl" symbol="profile" />
56
- <h2 className="form-card__title">{t("authentication.forgotPassword.sendEmail")}</h2>
56
+ <h1 className="form-card__title">{t("authentication.forgotPassword.sendEmail")}</h1>
57
57
  </div>
58
58
 
59
59
  {Object.entries(errors).length > 0 && !networkError.error && (
@@ -22,7 +22,7 @@ const AdditionalFees = ({
22
22
  footerContent,
23
23
  strings,
24
24
  }: AdditionalFeesProps) => {
25
- if (!deposit && !applicationFee && !strings && footerContent?.length === 0) return <></>
25
+ if (!deposit && !applicationFee && (!footerContent || footerContent?.length === 0)) return <></>
26
26
  return (
27
27
  <div className="info-card bg-gray-100 border-0">
28
28
  <p className="info-card__title mb-2">{strings.sectionHeader}</p>
@@ -86,3 +86,7 @@
86
86
  margin-inline-start: var(--bloom-s1);
87
87
  }
88
88
  }
89
+ .listings-pill_header {
90
+ margin-bottom: var(--bloom-s3);
91
+ margin-top: var(--bloom-s1);
92
+ }
@@ -5,7 +5,7 @@ import { StackedTable, StackedTableProps } from "../../tables/StackedTable"
5
5
  import { StandardTable, StandardTableProps } from "../../tables/StandardTable"
6
6
  import { Heading, HeaderType } from "../../headers/Heading"
7
7
  import { Tag } from "../../text/Tag"
8
- import { AppearanceStyleType } from "../../global/AppearanceTypes"
8
+ import { AppearanceShadeType, AppearanceStyleType } from "../../global/AppearanceTypes"
9
9
  import { Icon, IconFillColors } from "../../icons/Icon"
10
10
  import "./ListingCard.scss"
11
11
  import { NavigationContext } from "../../config/NavigationContext"
@@ -16,6 +16,8 @@ export interface CardHeader {
16
16
  content: string | React.ReactNode
17
17
  href?: string
18
18
  customClass?: string
19
+ styleType?: AppearanceStyleType
20
+ isPillType?: boolean
19
21
  }
20
22
 
21
23
  export interface FooterButton {
@@ -74,12 +76,25 @@ const ListingCard = (props: ListingCardProps) => {
74
76
  const getHeader = (
75
77
  header: CardHeader | undefined,
76
78
  priority: number,
77
- style?: HeaderType,
79
+ styleType?: HeaderType,
78
80
  customClass?: string
79
81
  ) => {
80
82
  if (header && header.content) {
83
+ if (header.isPillType) {
84
+ return (
85
+ <Tag
86
+ className="listings-pill_header"
87
+ pillStyle
88
+ capitalized
89
+ styleType={header.styleType}
90
+ shade={AppearanceShadeType.light}
91
+ >
92
+ {header.content}
93
+ </Tag>
94
+ )
95
+ }
81
96
  return (
82
- <Heading priority={priority} style={style} className={customClass}>
97
+ <Heading priority={priority} style={styleType} className={customClass}>
83
98
  {header.href ? (
84
99
  <LinkComponent className="is-card-link" href={header.href}>
85
100
  {header.content}
@@ -95,12 +95,12 @@ const Contact = ({
95
95
 
96
96
  {additionalInformation?.map((info) => {
97
97
  return (
98
- <React.Fragment key={info.title}>
98
+ <div key={info.title} className={"my-3"}>
99
99
  <Heading priority={3} style={"sidebarSubHeader"}>
100
100
  {info.title}
101
101
  </Heading>
102
102
  <div className="text-gray-800 text-tiny markdown">{info.content}</div>
103
- </React.Fragment>
103
+ </div>
104
104
  )
105
105
  })}
106
106
  </section>
@@ -1,13 +1,13 @@
1
1
  import React, { useState } from "react"
2
- import { t } from "../../../helpers/translator"
2
+ import Markdown from "markdown-to-jsx"
3
3
  import { Button } from "../../../actions/Button"
4
4
  import { LinkButton } from "../../../actions/LinkButton"
5
5
  import { AppearanceStyleType } from "../../../global/AppearanceTypes"
6
6
  import { Address } from "../../../helpers/MultiLineAddress"
7
- import { ContactAddress } from "./ContactAddress"
8
- import { OrDivider } from "./OrDivider"
9
7
  import { Heading } from "../../../headers/Heading"
10
- import Markdown from "markdown-to-jsx"
8
+ import { t } from "../../../helpers/translator"
9
+ import { OrDivider } from "./OrDivider"
10
+ import { ContactAddress } from "./ContactAddress"
11
11
 
12
12
  export interface PaperApplication {
13
13
  fileURL: string
@@ -33,6 +33,16 @@ export interface ApplicationsProps {
33
33
  postmarkedApplicationsReceivedByDate?: string
34
34
  /** Whether or not to hide actionable application buttons */
35
35
  preview?: boolean
36
+ strings?: {
37
+ applicationsOpenInFuture?: string
38
+ applyOnline?: string
39
+ downloadApplication?: string
40
+ getAPaperApplication?: string
41
+ getDirections?: string
42
+ howToApply?: string
43
+ officeHoursHeading?: string
44
+ pickUpApplication?: string
45
+ }
36
46
  }
37
47
  /** Displays information regarding how to apply, including an online application link button, paper application downloads, and a paper application pickup address */
38
48
  const GetApplication = (props: ApplicationsProps) => {
@@ -46,19 +56,22 @@ const GetApplication = (props: ApplicationsProps) => {
46
56
 
47
57
  return (
48
58
  <section className="aside-block" data-test-id="get-application-section">
49
- <h2 className="text-caps-underline">{t("listings.apply.howToApply")}</h2>
59
+ <h2 className="text-caps-underline">
60
+ {props.strings?.howToApply ?? t("listings.apply.howToApply")}
61
+ </h2>
50
62
  {!props.applicationsOpen && (
51
63
  <p className="mb-5 text-gray-700">
52
- {t("listings.apply.applicationWillBeAvailableOn", {
53
- openDate: props.applicationsOpenDate,
54
- })}
64
+ {props.strings?.applicationsOpenInFuture ??
65
+ t("listings.apply.applicationWillBeAvailableOn", {
66
+ openDate: props.applicationsOpenDate,
67
+ })}
55
68
  </p>
56
69
  )}
57
70
  {props.applicationsOpen && props.onlineApplicationURL && (
58
71
  <>
59
72
  {props.preview ? (
60
73
  <Button disabled className="w-full mb-2" data-test-id={"listing-view-apply-button"}>
61
- {t("listings.apply.applyOnline")}
74
+ {props.strings?.applyOnline ?? t("listings.apply.applyOnline")}
62
75
  </Button>
63
76
  ) : (
64
77
  <LinkButton
@@ -67,7 +80,7 @@ const GetApplication = (props: ApplicationsProps) => {
67
80
  href={props.onlineApplicationURL}
68
81
  dataTestId={"listing-view-apply-button"}
69
82
  >
70
- {t("listings.apply.applyOnline")}
83
+ {props.strings?.applyOnline ?? t("listings.apply.applyOnline")}
71
84
  </LinkButton>
72
85
  )}
73
86
  </>
@@ -76,7 +89,9 @@ const GetApplication = (props: ApplicationsProps) => {
76
89
  {props.applicationsOpen && props.paperMethod && !!props.paperApplications?.length && (
77
90
  <>
78
91
  {props.onlineApplicationURL && <OrDivider bgColor="white" />}
79
- <div className="text-serif-lg">{t("listings.apply.getAPaperApplication")}</div>
92
+ <div className="text-serif-lg">
93
+ {props.strings?.getAPaperApplication ?? t("listings.apply.getAPaperApplication")}
94
+ </div>
80
95
  <Button
81
96
  styleType={
82
97
  !props.preview && props.onlineApplicationURL ? AppearanceStyleType.primary : undefined
@@ -85,7 +100,7 @@ const GetApplication = (props: ApplicationsProps) => {
85
100
  onClick={toggleDownload}
86
101
  disabled={props.preview}
87
102
  >
88
- {t("listings.apply.downloadApplication")}
103
+ {props.strings?.downloadApplication ?? t("listings.apply.downloadApplication")}
89
104
  </Button>
90
105
  </>
91
106
  )}
@@ -94,7 +109,7 @@ const GetApplication = (props: ApplicationsProps) => {
94
109
  <p key={index} className="text-center mt-2 mb-4 text-sm">
95
110
  <a
96
111
  href={paperApplication.fileURL}
97
- title={t("listings.apply.downloadApplication")}
112
+ title={props.strings?.downloadApplication ?? t("listings.apply.downloadApplication")}
98
113
  target="_blank"
99
114
  >
100
115
  {paperApplication.languageString}
@@ -107,16 +122,16 @@ const GetApplication = (props: ApplicationsProps) => {
107
122
  <OrDivider bgColor="white" />
108
123
  )}
109
124
  <Heading priority={3} style={"sidebarSubHeader"}>
110
- {t("listings.apply.pickUpAnApplication")}
125
+ {props.strings?.pickUpApplication ?? t("listings.apply.pickUpAnApplication")}
111
126
  </Heading>
112
127
  <ContactAddress
113
128
  address={props.applicationPickUpAddress}
114
- mapString={t("t.getDirections")}
129
+ mapString={props.strings?.getDirections ?? t("t.getDirections")}
115
130
  />
116
131
  {props.applicationPickUpAddressOfficeHours && (
117
132
  <>
118
133
  <Heading priority={3} style={"sidebarSubHeader"}>
119
- {t("leasingAgent.officeHours")}
134
+ {props.strings?.officeHoursHeading ?? t("leasingAgent.officeHours")}
120
135
  </Heading>
121
136
  <p className="text-gray-800 text-tiny markdown">
122
137
  <Markdown
@@ -4,6 +4,9 @@ import dayjs from "dayjs"
4
4
 
5
5
  interface ListingUpdatedProps {
6
6
  listingUpdated: Date
7
+ strings?: {
8
+ listingUpdated?: string
9
+ }
7
10
  }
8
11
 
9
12
  const ListingUpdated = (props: ListingUpdatedProps) => {
@@ -11,7 +14,8 @@ const ListingUpdated = (props: ListingUpdatedProps) => {
11
14
  return (
12
15
  <section className="aside-block">
13
16
  <p className="text-tiny text-gray-800">
14
- {`${t("listings.listingUpdated")}: ${dayjs(listingUpdated).format("MMMM DD, YYYY")}`}
17
+ {props?.strings?.listingUpdated ??
18
+ `${t("listings.listingUpdated")}: ${dayjs(listingUpdated).format("MMMM DD, YYYY")}`}
15
19
  </p>
16
20
  </section>
17
21
  )
@@ -1,9 +1,11 @@
1
1
  import * as React from "react"
2
2
  import { t } from "../../../helpers/translator"
3
3
 
4
- const OrDivider = (props: { bgColor: string }) => (
4
+ const OrDivider = (props: { bgColor: string; strings?: { orString?: string } }) => (
5
5
  <div className="aside-block__divider">
6
- <span className={`bg-${props.bgColor} aside-block__conjunction`}>{t("t.or")}</span>
6
+ <span className={`bg-${props.bgColor} aside-block__conjunction`}>
7
+ {props.strings?.orString ?? t("t.or")}
8
+ </span>
7
9
  </div>
8
10
  )
9
11