@bloom-housing/ui-components 3.0.1-alpha.14 → 3.0.1-alpha.18

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.
@@ -1,4 +1,5 @@
1
1
  import "@testing-library/jest-dom/extend-expect"
2
+ import { configure } from "@testing-library/dom"
2
3
 
3
4
  import { addTranslation } from "../src/helpers/translator"
4
5
  import general from "../src/locales/general.json"
@@ -18,3 +19,5 @@ window.matchMedia = jest.fn().mockImplementation((query) => {
18
19
  })
19
20
 
20
21
  addTranslation(general)
22
+
23
+ configure({ testIdAttribute: "data-test-id" })
package/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.0.1-alpha.18](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@3.0.1-alpha.17...@bloom-housing/ui-components@3.0.1-alpha.18) (2021-11-23)
7
+
8
+
9
+ ### Features
10
+
11
+ * new demographics sub-race questions ([#2109](https://github.com/bloom-housing/bloom/issues/2109)) ([9ab8926](https://github.com/bloom-housing/bloom/commit/9ab892694c1ad2fa8890b411b3b32af68ade1fc3))
12
+
13
+
14
+
15
+
16
+
17
+ ## [3.0.1-alpha.17](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@3.0.1-alpha.16...@bloom-housing/ui-components@3.0.1-alpha.17) (2021-11-22)
18
+
19
+ **Note:** Version bump only for package @bloom-housing/ui-components
20
+
21
+
22
+
23
+
24
+
25
+ ## [3.0.1-alpha.16](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@3.0.1-alpha.15...@bloom-housing/ui-components@3.0.1-alpha.16) (2021-11-22)
26
+
27
+ **Note:** Version bump only for package @bloom-housing/ui-components
28
+
29
+
30
+
31
+
32
+
33
+ ## [3.0.1-alpha.15](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@3.0.1-alpha.14...@bloom-housing/ui-components@3.0.1-alpha.15) (2021-11-16)
34
+
35
+ **Note:** Version bump only for package @bloom-housing/ui-components
36
+
37
+
38
+
39
+
40
+
6
41
  ## [3.0.1-alpha.14](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@3.0.1-alpha.13...@bloom-housing/ui-components@3.0.1-alpha.14) (2021-11-16)
7
42
 
8
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloom-housing/ui-components",
3
- "version": "3.0.1-alpha.14",
3
+ "version": "3.0.1-alpha.18",
4
4
  "author": "Sean Albert <sean.albert@exygy.com>",
5
5
  "description": "Shared user interface components for Bloom affordable housing system",
6
6
  "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components",
@@ -102,5 +102,5 @@
102
102
  "peerDependencies": {
103
103
  "@bloom-housing/backend-core": ">= 2.0.0-pre-tailwind"
104
104
  },
105
- "gitHead": "410184fa2d0a748f9b84015502a37ba60d4c3e02"
105
+ "gitHead": "dfc1e8d88a9a0fe4e247b3355b4175a6d3b68a5a"
106
106
  }
@@ -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 {
@@ -49,6 +57,87 @@ const FieldGroup = ({
49
57
  fieldGroupClassName = `${fieldGroupClassName} flex`
50
58
  fieldClassName = `${fieldClassName} flex-initial mr-4`
51
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
+ }
52
141
  return (
53
142
  <>
54
143
  {groupLabel && <label className="field-label--caps">{groupLabel}</label>}
@@ -57,22 +146,14 @@ const FieldGroup = ({
57
146
  <div className={`field ${error && "error"} ${fieldGroupClassName || ""} mb-0`}>
58
147
  {fields?.map((item) => (
59
148
  <div className={`field ${fieldClassName || ""} mb-1`} key={item.id}>
60
- <input
61
- aria-describedby={`${name}-error`}
62
- aria-invalid={!!error || false}
63
- type={type}
64
- id={item.id}
65
- defaultValue={item.value || item.id}
66
- name={name}
67
- defaultChecked={item.defaultChecked || false}
68
- ref={register(validation)}
69
- {...item.inputProps}
70
- data-test-id={dataTestId}
71
- />
72
- <label htmlFor={item.id} className={`font-semibold ${fieldLabelClassName}`}>
73
- {item.label}
74
- </label>
75
- {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
+ )}
76
157
  </div>
77
158
  ))}
78
159
  </div>
@@ -63,6 +63,7 @@ export const Select = ({
63
63
  data-test-id={dataTestId}
64
64
  aria-describedby={describedBy ? describedBy : `${id}-error`}
65
65
  aria-invalid={!!error || false}
66
+ aria-label={label}
66
67
  ref={register && register(validation)}
67
68
  disabled={disabled}
68
69
  defaultValue={defaultValue ?? ""}
@@ -3,6 +3,7 @@ import {
3
3
  ApplicationSubmissionType,
4
4
  Language,
5
5
  ApplicationPreference,
6
+ ApplicationProgram,
6
7
  } from "@bloom-housing/backend-core/types"
7
8
 
8
9
  export const blankApplication = () => {
@@ -99,12 +100,13 @@ export const blankApplication = () => {
99
100
  preferredUnit: [],
100
101
  demographics: {
101
102
  ethnicity: "",
102
- race: "",
103
+ race: [],
103
104
  gender: "",
104
105
  sexualOrientation: "",
105
106
  howDidYouHear: [],
106
107
  },
107
108
  preferences: [] as ApplicationPreference[],
109
+ programs: [] as ApplicationProgram[],
108
110
  confirmationCode: "",
109
111
  id: "",
110
112
  }
@@ -144,6 +144,7 @@
144
144
  "agency": "Agency if Applicable",
145
145
  "adaPriorities": "ADA Priorities Selected",
146
146
  "preferences": "Application Preferences",
147
+ "programs": "Application Programs",
147
148
  "liveOrWorkIn": "Live or Work in",
148
149
  "householdIncome": "Declared Household Income",
149
150
  "annualIncome": "Annual Income",
@@ -378,6 +379,49 @@
378
379
  "legend": "Housing vouchers, nontaxable income or rental subsidies"
379
380
  }
380
381
  },
382
+ "programs": {
383
+ "servedInMilitary": {
384
+ "summary": "Veteran of the US Military",
385
+ "servedInMilitary": {
386
+ "label": "Yes"
387
+ },
388
+ "doNotConsider": {
389
+ "label": "No"
390
+ }
391
+ },
392
+ "tay": {
393
+ "summary": "Transition Age Youth (TAY)",
394
+ "tay": {
395
+ "label": "Yes"
396
+ },
397
+ "doNotConsider": {
398
+ "label": "No"
399
+ }
400
+ },
401
+ "disabilityOrMentalIllness": {
402
+ "summary": "People with Developmental Disabilities or Mental Illness",
403
+ "disabilityOrMentalIllness": {
404
+ "label": "Yes"
405
+ },
406
+ "doNotConsider": {
407
+ "label": "No"
408
+ }
409
+ },
410
+ "housingSituation": {
411
+ "summary": "Temporarily Housed or Homeless",
412
+ "notPermanent": {
413
+ "label": "I have somewhere to stay, but it isn't permanent",
414
+ "description": "Includes staying with friends or family, living in a motel / hotel, or living in a medical or other facility and those who have received an eviction notice or will imminently lose their current residence"
415
+ },
416
+ "homeless": {
417
+ "label": "I'm homeless",
418
+ "description": "Includes living outside, or in your car, or staying at a shelter, or in a motel / hotel paid for with an emergency voucher"
419
+ },
420
+ "doNotConsider": {
421
+ "label": "No"
422
+ }
423
+ }
424
+ },
381
425
  "preferences": {
382
426
  "title": "Your household may qualify for the following housing preferences.",
383
427
  "preamble": "If you qualify for this preference, you'll get a higher ranking.",
@@ -623,15 +667,28 @@
623
667
  },
624
668
  "raceOptions": {
625
669
  "americanIndianAlaskanNative": "American Indian / Alaskan Native",
626
- "asian": "Asian",
627
- "blackAfricanAmerican": "Black / African American",
628
- "nativeHawaiianOtherPacificIslander": "Native Hawaiian / Other Pacific Islander",
629
- "white": "White",
630
670
  "americanIndianAlaskanNativeAndBlackAfricanAmerican": "American Indian / Alaskan Native and Black/African American",
631
671
  "americanIndianAlaskanNativeAndWhite": "American Indian / Alaskan Native and White",
672
+ "asian": "Asian",
632
673
  "asianAndWhite": "Asian and White",
674
+ "asian-asianIndian": "Asian Indian",
675
+ "asian-otherAsian": "Other Asian",
676
+ "blackAfricanAmerican": "Black / African American",
633
677
  "blackAfricanAmericanAndWhite": "Black / African American and White",
634
- "otherMutliracial": "Other / Mutliracial"
678
+ "asian-chinese": "Chinese",
679
+ "declineToRespond": "Decline to Respond",
680
+ "asian-filipino": "Filipino",
681
+ "nativeHawaiianOtherPacificIslander-guamanianOrChamorro": "Guamanian or Chamorro",
682
+ "asian-japanese": "Japanese",
683
+ "asian-korean": "Korean",
684
+ "nativeHawaiianOtherPacificIslander-nativeHawaiian": "Native Hawaiian",
685
+ "nativeHawaiianOtherPacificIslander": "Native Hawaiian / Other Pacific Islander",
686
+ "otherMultiracial": "Other / Multiracial",
687
+ "otherMutliracial": "Other / Mutliracial",
688
+ "nativeHawaiianOtherPacificIslander-otherPacificIslander": "Other Pacific Islander",
689
+ "nativeHawaiianOtherPacificIslander-samoan": "Samoan",
690
+ "asian-vietnamese": "Vietnamese",
691
+ "white": "White"
635
692
  },
636
693
  "genderOptions": {
637
694
  "female": "Female",
@@ -1110,6 +1167,7 @@
1110
1167
  "communityType": "Community Type",
1111
1168
  "communityTypeSubtitle": "Are there any requirements that applicants need to meet?",
1112
1169
  "costsNotIncluded": "Costs Not Included",
1170
+ "depositHelperText": "Deposit Helper Text",
1113
1171
  "eligibilitySubtitle": "Income, occupancy, preferences, and subsidies",
1114
1172
  "eligibilityTitle": "Eligibility",
1115
1173
  "featuresSubtitle": "Amenities, unit details and additional fees",
@@ -1442,6 +1500,7 @@
1442
1500
  "phone": "Phone",
1443
1501
  "phoneNumberPlaceholder": "(555) 555-5555",
1444
1502
  "pleaseSelectOne": "Please select one.",
1503
+ "preferNotToSay": "Prefer not to say",
1445
1504
  "pm": "PM",
1446
1505
  "post": "Post",
1447
1506
  "preferences": "Preferences",
@@ -6,6 +6,7 @@ export interface AdditionalFeesProps {
6
6
  depositMax?: string
7
7
  applicationFee?: string
8
8
  costsNotIncluded?: string
9
+ depositHelperText?: string
9
10
  }
10
11
 
11
12
  const AdditionalFees = (props: AdditionalFeesProps) => {
@@ -37,7 +38,7 @@ const AdditionalFees = (props: AdditionalFeesProps) => {
37
38
  <div className="info-card__column">
38
39
  <div className="text-base">{t("t.deposit")}</div>
39
40
  <div className="text-xl font-bold">{getDeposit()}</div>
40
- <div>{t("listings.depositOrMonthsRent")}</div>
41
+ {props.depositHelperText && <div>{props.depositHelperText}</div>}
41
42
  </div>
42
43
  )}
43
44
  </div>