@bloom-housing/ui-components 3.0.1-alpha.6 → 4.0.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 (52) hide show
  1. package/.jest/setup-tests.js +3 -0
  2. package/CHANGELOG.md +406 -0
  3. package/index.ts +1 -8
  4. package/jest.config.js +0 -1
  5. package/package.json +4 -4
  6. package/src/actions/Button.tsx +6 -0
  7. package/src/actions/LinkButton.tsx +7 -2
  8. package/src/blocks/DashBlock.tsx +6 -1
  9. package/src/blocks/ViewItem.tsx +2 -1
  10. package/src/forms/DOBField.tsx +3 -0
  11. package/src/forms/DateField.tsx +4 -0
  12. package/src/forms/Field.tsx +3 -0
  13. package/src/forms/FieldGroup.tsx +100 -16
  14. package/src/forms/HouseholdMemberForm.tsx +2 -1
  15. package/src/forms/PhoneField.tsx +2 -1
  16. package/src/forms/Select.tsx +4 -0
  17. package/src/forms/TimeField.tsx +6 -0
  18. package/src/global/forms.scss +1 -0
  19. package/src/headers/Hero.scss +7 -0
  20. package/src/headers/Hero.tsx +3 -3
  21. package/src/headers/PageHeader.tsx +2 -2
  22. package/src/headers/SiteHeader.scss +6 -5
  23. package/src/icons/Icon.tsx +2 -1
  24. package/src/icons/Icons.tsx +7 -2
  25. package/src/locales/es.json +53 -6
  26. package/src/locales/general.json +188 -18
  27. package/src/locales/general_OLD.json +1 -1
  28. package/src/locales/missing-translations.ts +2 -0
  29. package/src/locales/tl.json +5 -0
  30. package/src/locales/vi.json +51 -5
  31. package/src/locales/zh.json +53 -7
  32. package/src/navigation/ProgressNav.tsx +5 -3
  33. package/src/notifications/AlertBox.tsx +1 -1
  34. package/src/notifications/ErrorMessage.tsx +6 -1
  35. package/src/overlays/Modal.scss +4 -1
  36. package/src/overlays/Modal.tsx +5 -6
  37. package/src/page_components/listing/AdditionalFees.tsx +2 -1
  38. package/src/page_components/listing/ListingCard.tsx +1 -1
  39. package/src/page_components/listing/listing_sidebar/GetApplication.tsx +8 -1
  40. package/src/page_components/listing/listing_sidebar/SubmitApplication.tsx +13 -3
  41. package/src/page_components/listing/listing_sidebar/Waitlist.tsx +31 -62
  42. package/src/page_components/listing/listing_sidebar/events/DownloadLotteryResults.tsx +4 -7
  43. package/src/tables/MinimalTable.tsx +2 -0
  44. package/src/tables/StandardTable.tsx +2 -1
  45. package/tsconfig.json +0 -1
  46. package/src/helpers/blankApplication.ts +0 -111
  47. package/src/helpers/localeRoute.tsx +0 -13
  48. package/src/helpers/nextjs.ts +0 -7
  49. package/src/helpers/occupancyFormatting.tsx +0 -46
  50. package/src/helpers/pdfs.ts +0 -19
  51. package/src/helpers/photos.ts +0 -19
  52. package/src/helpers/unitTypes.ts +0 -42
@@ -1,14 +1,22 @@
1
- import React from "react"
1
+ import React, { useState, useEffect } from "react"
2
+ import { ExpandableContent } from "../actions/ExpandableContent"
2
3
  import { ErrorMessage } from "../notifications/ErrorMessage"
3
4
  import { UseFormMethods } from "react-hook-form"
5
+ import { Field } from "./Field"
6
+ import { t } from "../helpers/translator"
4
7
 
5
8
  interface FieldSingle {
6
9
  id: string
7
10
  label: string
8
11
  value?: string
9
12
  defaultChecked?: boolean
13
+ description?: React.ReactNode
14
+ defaultText?: string
10
15
  note?: string
11
16
  inputProps?: Record<string, unknown>
17
+ subFields?: FieldSingle[]
18
+ type?: string
19
+ additionalText?: boolean
12
20
  }
13
21
 
14
22
  interface FieldGroupProps {
@@ -25,6 +33,7 @@ interface FieldGroupProps {
25
33
  fieldGroupClassName?: string
26
34
  fieldClassName?: string
27
35
  fieldLabelClassName?: string
36
+ dataTestId?: string
28
37
  }
29
38
 
30
39
  const FieldGroup = ({
@@ -41,12 +50,94 @@ const FieldGroup = ({
41
50
  fieldClassName,
42
51
  fieldLabelClassName,
43
52
  groupSubNote,
53
+ dataTestId,
44
54
  }: FieldGroupProps) => {
45
55
  // Always align two-option radio groups side by side
46
56
  if (fields?.length === 2) {
47
57
  fieldGroupClassName = `${fieldGroupClassName} flex`
48
58
  fieldClassName = `${fieldClassName} flex-initial mr-4`
49
59
  }
60
+
61
+ const [checkedInputs, setCheckedInputs] = useState<string[]>([])
62
+
63
+ const subfieldsExist = () => {
64
+ return fields?.filter((field) => field.subFields).length
65
+ }
66
+
67
+ const getIndividualInput = (item: FieldSingle): React.ReactNode => {
68
+ return (
69
+ <div key={item.value}>
70
+ <input
71
+ aria-describedby={`${name}-error`}
72
+ aria-invalid={!!error || false}
73
+ type={type}
74
+ id={item.id}
75
+ defaultValue={item.value || item.id}
76
+ name={subfieldsExist() ? `${name}-${item.value}` : name}
77
+ onClick={(e) => {
78
+ // We cannot reliably target an individual checkbox in a field group since they have the same name, so we keep track on our own
79
+ if (e.currentTarget.checked) {
80
+ setCheckedInputs([...checkedInputs, item.label])
81
+ } else {
82
+ setCheckedInputs(checkedInputs.filter((subset) => item.label !== subset))
83
+ }
84
+ }}
85
+ defaultChecked={item.defaultChecked || false}
86
+ ref={register(validation)}
87
+ {...item.inputProps}
88
+ data-test-id={dataTestId}
89
+ />
90
+ <label htmlFor={item.id} className={`font-semibold ${fieldLabelClassName}`}>
91
+ {item.label}
92
+ </label>
93
+ {item.note && <span className={"field-note font-normal"}>{item.note}</span>}
94
+
95
+ {item.description && (
96
+ <div className="ml-8 -mt-1 mb-5">
97
+ <ExpandableContent>
98
+ <p className="field-note mb-2 -mt-2">{item.description}</p>
99
+ </ExpandableContent>
100
+ </div>
101
+ )}
102
+ </div>
103
+ )
104
+ }
105
+
106
+ const checkSelected = (formFields: FieldSingle[] | undefined, checkedValues: string[]) => {
107
+ formFields?.forEach((field) => {
108
+ if (field.defaultChecked) {
109
+ checkedValues.push(field.label)
110
+ }
111
+ if (field.subFields) {
112
+ checkSelected(field.subFields, checkedValues)
113
+ }
114
+ })
115
+ }
116
+
117
+ useEffect(() => {
118
+ const initialValues: string[] = []
119
+ checkSelected(fields, initialValues)
120
+ setCheckedInputs([...initialValues])
121
+ }, [])
122
+
123
+ const getInputSet = (item: FieldSingle): React.ReactNode => {
124
+ return (
125
+ <div key={item.value}>
126
+ {getIndividualInput(item)}
127
+ {item.additionalText && checkedInputs.indexOf(item.label) >= 0 && (
128
+ <Field
129
+ id={item.id}
130
+ key={`${item.value}-additionalText`}
131
+ name={`${name}-${item.value}`}
132
+ register={register}
133
+ defaultValue={item.defaultText}
134
+ placeholder={t("t.description")}
135
+ className={"mb-4"}
136
+ />
137
+ )}
138
+ </div>
139
+ )
140
+ }
50
141
  return (
51
142
  <>
52
143
  {groupLabel && <label className="field-label--caps">{groupLabel}</label>}
@@ -55,21 +146,14 @@ const FieldGroup = ({
55
146
  <div className={`field ${error && "error"} ${fieldGroupClassName || ""} mb-0`}>
56
147
  {fields?.map((item) => (
57
148
  <div className={`field ${fieldClassName || ""} mb-1`} key={item.id}>
58
- <input
59
- aria-describedby={`${name}-error`}
60
- aria-invalid={!!error || false}
61
- type={type}
62
- id={item.id}
63
- defaultValue={item.value || item.id}
64
- name={name}
65
- defaultChecked={item.defaultChecked || false}
66
- ref={register(validation)}
67
- {...item.inputProps}
68
- />
69
- <label htmlFor={item.id} className={`font-semibold ${fieldLabelClassName}`}>
70
- {item.label}
71
- </label>
72
- {item.note && <span className={"field-note font-normal"}>{item.note}</span>}
149
+ {getInputSet(item)}
150
+ {item.subFields && checkedInputs.indexOf(item.label) >= 0 && (
151
+ <div className={"ml-8"} key={`${item.value}-subfields`}>
152
+ {item.subFields?.map((subItem) => {
153
+ return getInputSet(subItem)
154
+ })}
155
+ </div>
156
+ )}
73
157
  </div>
74
158
  ))}
75
159
  </div>
@@ -20,10 +20,11 @@ const HouseholdMemberForm = (props: HouseholdMemberFormProps) => {
20
20
  {props.memberFirstName} {props.memberLastName}
21
21
  {editMode ? (
22
22
  <button
23
- id="edit-member"
23
+ id={`edit-member-${props.memberFirstName}-${props.memberLastName}`}
24
24
  className="edit-link"
25
25
  onClick={() => props.editMember && props.editMember(props.memberId)}
26
26
  type={"button"}
27
+ data-test-id={"app-household-member-edit-button"}
27
28
  >
28
29
  {t("t.edit")}
29
30
  </button>
@@ -19,6 +19,7 @@ export const PhoneField = (props: {
19
19
  disabled?: boolean
20
20
  required?: boolean
21
21
  mask?: (args: any) => JSX.Element
22
+ dataTestId?: string
22
23
  }) => {
23
24
  const labelClasses = ["label"]
24
25
  if (props.caps) labelClasses.push("field-label--caps")
@@ -55,7 +56,7 @@ export const PhoneField = (props: {
55
56
  return (
56
57
  <div className={"field " + (props.error ? "error" : "")}>
57
58
  {props.label && <label className={labelClasses.join(" ")}>{props.label}</label>}
58
- <div className={props.controlClassName}>
59
+ <div className={props.controlClassName} data-test-id={props.dataTestId}>
59
60
  {props.mask ? (
60
61
  <Controller {...controllerProps} render={props.mask} />
61
62
  ) : (
@@ -27,6 +27,7 @@ interface SelectProps {
27
27
  keyPrefix?: string
28
28
  describedBy?: string
29
29
  inputProps?: Record<string, unknown>
30
+ dataTestId?: string
30
31
  }
31
32
 
32
33
  export const Select = ({
@@ -47,6 +48,7 @@ export const Select = ({
47
48
  inputProps,
48
49
  defaultValue,
49
50
  subNote,
51
+ dataTestId,
50
52
  }: SelectProps) => {
51
53
  return (
52
54
  <div className={`field ${error ? "error" : ""}`}>
@@ -58,8 +60,10 @@ export const Select = ({
58
60
  className="input"
59
61
  id={id || name}
60
62
  name={name}
63
+ data-test-id={dataTestId}
61
64
  aria-describedby={describedBy ? describedBy : `${id}-error`}
62
65
  aria-invalid={!!error || false}
66
+ aria-label={label}
63
67
  ref={register && register(validation)}
64
68
  disabled={disabled}
65
69
  defaultValue={defaultValue ?? ""}
@@ -28,6 +28,7 @@ export type TimeFieldProps = {
28
28
  required?: boolean
29
29
  watch: UseFormMethods["watch"]
30
30
  seconds?: boolean
31
+ dataTestId?: string
31
32
  }
32
33
 
33
34
  export const formatDateToTimeField = (date: Date) => {
@@ -54,6 +55,7 @@ const TimeField = ({
54
55
  seconds,
55
56
  defaultValues,
56
57
  disabled,
58
+ dataTestId,
57
59
  }: TimeFieldProps) => {
58
60
  const fieldName = (baseName: string) => {
59
61
  return [name, baseName].filter((item) => item).join(".")
@@ -99,6 +101,7 @@ const TimeField = ({
99
101
  register={register}
100
102
  describedBy={`${id}-error`}
101
103
  disabled={disabled}
104
+ dataTestId={dataTestId ? `${dataTestId}-hours` : undefined}
102
105
  />
103
106
 
104
107
  <Field
@@ -122,6 +125,7 @@ const TimeField = ({
122
125
  register={register}
123
126
  describedBy={`${id}-error`}
124
127
  disabled={disabled}
128
+ dataTestId={dataTestId ? `${dataTestId}-minutes` : undefined}
125
129
  />
126
130
 
127
131
  {seconds && (
@@ -146,6 +150,7 @@ const TimeField = ({
146
150
  register={register}
147
151
  describedBy={`${id}-error`}
148
152
  disabled={disabled}
153
+ dataTestId={dataTestId ? `${dataTestId}-seconds` : undefined}
149
154
  />
150
155
  )}
151
156
 
@@ -161,6 +166,7 @@ const TimeField = ({
161
166
  error={error}
162
167
  describedBy={`${id}-error`}
163
168
  disabled={disabled}
169
+ dataTestId={dataTestId ? `${dataTestId}-period` : undefined}
164
170
  />
165
171
  </div>
166
172
 
@@ -323,6 +323,7 @@ input[type="number"] {
323
323
  @apply text-tiny;
324
324
  @apply text-gray-700;
325
325
  @apply font-semibold;
326
+ white-space: pre-line;
326
327
  }
327
328
 
328
329
  .error-message {
@@ -20,6 +20,13 @@
20
20
  width: 100%;
21
21
  }
22
22
 
23
+ .hero__buttons {
24
+ @apply grid;
25
+ @apply md:grid-cols-4;
26
+ @apply gap-5;
27
+ @apply max-w-screen-md;
28
+ @apply m-auto;
29
+ }
23
30
  .hero__title {
24
31
  @apply text-4xl;
25
32
  @apply mb-5;
@@ -37,7 +37,7 @@ const Hero = (props: HeroProps) => {
37
37
  classNames = "centered"
38
38
  }
39
39
  return (
40
- <div className={`hero ${classNames}`} style={styles}>
40
+ <div className={`hero ${classNames}`} style={styles} data-test-id={"hero-component"}>
41
41
  <h1 className={`hero__title ${props.extraLargeTitle ? "lg:text-6.5xl" : ""}`}>
42
42
  {props.title}
43
43
  </h1>
@@ -46,9 +46,9 @@ const Hero = (props: HeroProps) => {
46
46
  {props.buttonTitle && props.buttonLink && (
47
47
  <>
48
48
  {props.secondaryButtonTitle && props.secondaryButtonLink ? (
49
- <div className="grid md:grid-cols-6 gap-5 max-w-screen-lg m-auto">
49
+ <div className="hero__buttons">
50
50
  <HeroButton
51
- className={"md:col-start-3 with_secondary"}
51
+ className={"md:col-start-2 with_secondary"}
52
52
  href={props.buttonLink}
53
53
  title={props.buttonTitle}
54
54
  />
@@ -4,7 +4,7 @@ import "./PageHeader.scss"
4
4
  export interface PageHeaderProps {
5
5
  className?: string
6
6
  inverse?: boolean
7
- title: React.ReactNode
7
+ title?: React.ReactNode
8
8
  subtitle?: string
9
9
  children?: React.ReactNode
10
10
  tabNav?: React.ReactNode
@@ -26,7 +26,7 @@ const PageHeader = (props: PageHeaderProps) => {
26
26
  return (
27
27
  <header className={classNames.join(" ")}>
28
28
  <hgroup className="page-header__group">
29
- <h1 className="page-header__title">{props.title}</h1>
29
+ {props.title && <h1 className="page-header__title">{props.title}</h1>}
30
30
  {props.subtitle && <p className="page-header__lead"> {props.subtitle}</p>}
31
31
  {props.children}
32
32
 
@@ -322,7 +322,10 @@
322
322
  .navbar-notice {
323
323
  @apply w-full;
324
324
  @apply bg-primary;
325
- @apply text-right;
325
+ @screen md {
326
+ @apply text-right;
327
+ }
328
+ @apply text-left;
326
329
  @apply text-white;
327
330
  @apply block;
328
331
  }
@@ -338,11 +341,9 @@
338
341
  .navbar-notice__text {
339
342
  @apply max-w-5xl;
340
343
  @apply text-sm;
341
- @apply pt-2;
342
- @apply pb-2;
343
- @apply pr-3;
344
+ @apply py-2;
345
+ @apply px-3;
344
346
  margin: auto;
345
- height: 36px;
346
347
 
347
348
  a,
348
349
  a:hover {
@@ -133,6 +133,7 @@ export interface IconProps {
133
133
  symbol: IconTypes
134
134
  className?: string
135
135
  fill?: string
136
+ ariaHidden?: boolean
136
137
  }
137
138
 
138
139
  const Icon = (props: IconProps) => {
@@ -144,7 +145,7 @@ const Icon = (props: IconProps) => {
144
145
  const SpecificIcon = IconMap[props.symbol]
145
146
 
146
147
  return (
147
- <span className={wrapperClasses.join(" ")}>
148
+ <span className={wrapperClasses.join(" ")} aria-hidden={props.ariaHidden}>
148
149
  <SpecificIcon fill={props.fill ? props.fill : undefined} />
149
150
  </span>
150
151
  )
@@ -474,9 +474,14 @@ export const MailThin = (props: IconProps) => {
474
474
  fill={props.fill ?? "none"}
475
475
  xmlns="http://www.w3.org/2000/svg"
476
476
  >
477
- <rect width="80" height="75" fill="#E5E5E5" />
477
+ <rect width="80" height="75" fill={props.fill ?? "none"} />
478
478
  <g clipPath="url(#clip0)">
479
- <rect width="1280" height="1390" transform="translate(-284 -530)" fill="white" />
479
+ <rect
480
+ width="1280"
481
+ height="1390"
482
+ transform="translate(-284 -530)"
483
+ fill={props.fill ?? "none"}
484
+ />
480
485
  <path
481
486
  d="M61.3749 24.2402L43.2061 39.7424C41.8262 40.8341 39.7563 40.8341 38.3764 39.7424L19.9775 24.2402"
482
487
  stroke="black"
@@ -130,10 +130,12 @@
130
130
  "assistanceUrl": "https://exygy.com/",
131
131
  "dontQualifyHeader": "Desafortunadamente, parece que usted no reúne los requisitos de este listado.",
132
132
  "dontQualifyInfo": "Por favor haga cambios si usted cree haber cometido algún error. Tenga presente que si usted falsifica cualquier tipo de información en su solicitud será descalificado(a). Si la información que usted ingresó es correcta, lo invitamos a visitarnos en el futuro a medida que otras propiedades están disponibles.",
133
+ "genericSubtitle": "Si se eligiera su solicitud, esté preparado para proporcionar documentación respaldatoria.",
133
134
  "addMembers": {
134
135
  "addHouseholdMember": "+ Añadir a un miembro del hogar",
135
136
  "done": "Ya terminó de añadir personas",
136
- "title": "Háblenos un poco acerca de su hogar."
137
+ "title": "Háblenos un poco acerca de su hogar.",
138
+ "doubleCheck": "Por favor revise la información de cada miembro del hogar."
137
139
  },
138
140
  "householdMember": "Miembro del hogar",
139
141
  "householdMembers": "Miembros del hogar",
@@ -145,6 +147,7 @@
145
147
  "preferredUnit": {
146
148
  "preferredUnitType": "Tipo de vivienda preferida",
147
149
  "title": "¿Cuáles son los tamaños de vivienda que le interesan?",
150
+ "subTitle": "Aunque los tamaños de las unidades en general se basen en la ocupación, indique el tamaño de unidad que desee para determinar su preferencia en esta oportunidad o establecer una lista de espera (solo por esta oportunidad).",
148
151
  "optionsLabel": "Marque todas las opciones que correspondan:",
149
152
  "options": {
150
153
  "studio": "Estudio",
@@ -154,6 +157,14 @@
154
157
  "fourBdrm": "+ de 3 dormitorios"
155
158
  }
156
159
  },
160
+ "expectingChanges": {
161
+ "title": "Espera cambios en su grupo familiar",
162
+ "question": "¿Espera que haya cambios en su grupo familiar en los próximos 12 meses?"
163
+ },
164
+ "householdStudent": {
165
+ "title": "El grupo familiar incluye un estudiante o miembro que está por cumplir 18 años",
166
+ "question": "¿Alguien de su grupo familiar es estudiante a tiempo completo o cumplirá 18 años en los próximos 60 días?"
167
+ },
157
168
  "member": {
158
169
  "cancelAddingThisPerson": "Cancelar añadir a esta persona",
159
170
  "deleteThisPerson": "Borrar a esta persona",
@@ -277,7 +288,21 @@
277
288
  "americanIndianAlaskanNativeAndWhite": "Indígena norteamericano o nativo de Alaska y blanco",
278
289
  "asianAndWhite": "Asiático y blanco",
279
290
  "blackAfricanAmericanAndWhite": "Negro o afroamericano y blanco",
280
- "otherMutliracial": "Otro / Multirracial"
291
+ "otherMutliracial": "Otro / Multirracial",
292
+ "asian-otherAsian": "De otro país asiático",
293
+ "asian-chinese": "Chino",
294
+ "asian-asianIndian": "Indio asiático",
295
+ "declineToRespond": "No desea responder",
296
+ "asian-filipino": "Filipino",
297
+ "nativeHawaiianOtherPacificIslander-guamanianOrChamorro": "Guameño o chamorro",
298
+ "asian-japanese": "Japonés",
299
+ "asian-korean": "Coreano",
300
+ "nativeHawaiianOtherPacificIslander-nativeHawaiian": "Nativo hawaiano",
301
+ "otherMultiracial": "Otro/multirracial",
302
+ "nativeHawaiianOtherPacificIslander-otherPacificIslander": "Otro habitante de una isla del Pacífico",
303
+ "nativeHawaiianOtherPacificIslander-samoan": "Samoano",
304
+ "asian-vietnamese": "Vietnamita"
305
+
281
306
  },
282
307
  "genderOptions": {
283
308
  "female": "Mujer",
@@ -295,7 +320,7 @@
295
320
  "notListed": "No aparece en la lista"
296
321
  },
297
322
  "howDidYouHearOptions": {
298
- "alamedaCountyHCDWebsite": "Sitio web del HCD del Condado de Alameda",
323
+ "jurisdictionWebsite": "Sitio web del HCD del Condado de Alameda",
299
324
  "developerWebsite": "Sitio web del constructor",
300
325
  "flyer": "Folleto",
301
326
  "emailAlert": "Aviso por email",
@@ -364,7 +389,22 @@
364
389
  "signIn": {
365
390
  "error": "Hubo un error cuando usted inició sesión",
366
391
  "errorGenericMessage": "Por favor inténtelo de nuevo, o comuníquese con servicio al cliente para recibir asistencia.",
392
+ "forgotPassword": "Olvidé la contraseña",
367
393
  "success": "¡Bienvenido de nuevo, %{name}!"
394
+ },
395
+ "createAccount": {
396
+ "accountConfirmed": "Su cuenta ha sido confirmada correctamente.",
397
+ "password": "Contraseña",
398
+ "noAccount": "No tienes una cuenta?",
399
+ "anEmailHasBeenSent": "Se ha enviado un email a %{email}",
400
+ "confirmationInstruction": "Por favor haga clic en el enlace del email que le hemos enviado para completar la creación de su cuenta.",
401
+ "confirmationNeeded": "Se necesita confirmación",
402
+ "resendTheEmail": "Volver a enviar el email",
403
+ "reEnterEmail": "Vuelva a introducir la dirección de correo electrónico",
404
+ "passwordInfo": "Debe tener al menos 8 caracteres e incluir al menos 1 letra y al menos un número",
405
+ "reEnterPassword": "Reingresa tu contraseña",
406
+ "mustBe8Chars": "debe tener 8 caracteres",
407
+ "yourName": "Su nombre"
368
408
  }
369
409
  },
370
410
  "errors": {
@@ -402,7 +442,7 @@
402
442
  "forGeneralQuestions": "Si requiere información general sobre el programa, nos puede llamar al 000-000-0000.",
403
443
  "giveFeedback": "Proporcione sus comentarios",
404
444
  "privacyPolicy": "Política de privacidad",
405
- "copyright": "Demonstration County © 2020 • Todos los derechos reservados."
445
+ "copyright": "Demonstration Jurisdiction © 2021 • Todos los derechos reservados."
406
446
  },
407
447
  "housingCounselors": {
408
448
  "subtitle": "Hable con un asesor de vivienda local en forma específica a sus necesidades.",
@@ -567,7 +607,8 @@
567
607
  },
568
608
  "nav": {
569
609
  "accountSettings": "Configuraciones de la cuenta",
570
- "browseProperties": "Ver Propiedades",
610
+ "browseProperties": "Buscar Propiedades",
611
+ "getFeedback": "Nos encantaría recibir sus comentarios",
571
612
  "getAssistance": "Obtener asistencia",
572
613
  "listings": "Listados",
573
614
  "properties": "Propiedades",
@@ -674,6 +715,7 @@
674
715
  "edit": "Editar",
675
716
  "email": "Email",
676
717
  "emailAddressPlaceholder": "you@myemail.com",
718
+ "description": "Ingrese descripción",
677
719
  "floor": "piso",
678
720
  "floors": "pisos",
679
721
  "getDirections": "Obtener instrucciones para llegar",
@@ -702,6 +744,7 @@
702
744
  "phone": "Teléfono",
703
745
  "phoneNumberPlaceholder": "(555) 555-5555",
704
746
  "pleaseSelectOne": "Por favor seleccione una opción.",
747
+ "pleaseSelectYesNo": "Elija “sí” o “no”.",
705
748
  "preferences": "Preferencias",
706
749
  "propertyAmenities": "Servicios en la propiedad",
707
750
  "range": "%{from} a %{to}",
@@ -737,7 +780,11 @@
737
780
  "seeRentalListings": "Ver viviendas en alquiler",
738
781
  "title": "Haga su solicitud de vivienda de precio accesible en",
739
782
  "seeMoreOpportunities": "Ver más oportunidades de alquiler y propiedad de vivienda",
740
- "viewAdditionalHousing": "Vea Oportunidades y recursos adicionales de vivienda"
783
+ "viewAdditionalHousing": "Vea Oportunidades y recursos adicionales de vivienda",
784
+ "seeMoreOpportunitiesTruncated": "Ver más oportunidades y recursos de vivienda",
785
+ "viewAdditionalHousingTruncated": "Ver oportunidades y recursos",
786
+ "signUp": "Reciba correos electrónicos cuando se publique un anuncio nuevo",
787
+ "signUpToday": "Regístrese hoy"
741
788
  },
742
789
  "whatToExpect": {
743
790
  "label": "Lo que puede esperar",