@bloom-housing/ui-components 4.1.3-alpha.1 → 4.1.3-alpha.4

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,36 @@
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
+ ## [4.1.3-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.1.3-alpha.3...@bloom-housing/ui-components@4.1.3-alpha.4) (2022-04-05)
7
+
8
+
9
+ ### Features
10
+
11
+ * open preference links in new tab/window ([#2618](https://github.com/bloom-housing/bloom/issues/2618)) ([ba07a7f](https://github.com/bloom-housing/bloom/commit/ba07a7ff2d01fadf1439671557ae44ce583426de))
12
+
13
+
14
+
15
+
16
+
17
+ ## [4.1.3-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.1.3-alpha.2...@bloom-housing/ui-components@4.1.3-alpha.3) (2022-04-04)
18
+
19
+ **Note:** Version bump only for package @bloom-housing/ui-components
20
+
21
+
22
+
23
+
24
+
25
+ ## [4.1.3-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.1.3-alpha.1...@bloom-housing/ui-components@4.1.3-alpha.2) (2022-04-04)
26
+
27
+
28
+ ### Features
29
+
30
+ * listing card title redesign, multiple tags possible ([#2531](https://github.com/bloom-housing/bloom/issues/2531)) ([2b795cb](https://github.com/bloom-housing/bloom/commit/2b795cb6c47c084937e996332d1583e9e5bcbc54))
31
+
32
+
33
+
34
+
35
+
6
36
  ## [4.1.3-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.1.3-alpha.0...@bloom-housing/ui-components@4.1.3-alpha.1) (2022-04-04)
7
37
 
8
38
  **Note:** Version bump only for package @bloom-housing/ui-components
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloom-housing/ui-components",
3
- "version": "4.1.3-alpha.1",
3
+ "version": "4.1.3-alpha.4",
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",
@@ -69,7 +69,7 @@
69
69
  "webpack": "^4.44.2"
70
70
  },
71
71
  "dependencies": {
72
- "@bloom-housing/backend-core": "^4.1.3-alpha.0",
72
+ "@bloom-housing/backend-core": "^4.1.3-alpha.1",
73
73
  "@mapbox/mapbox-sdk": "^0.13.0",
74
74
  "@types/body-scroll-lock": "^2.6.1",
75
75
  "@types/jwt-decode": "^2.2.1",
@@ -80,7 +80,7 @@
80
80
  "@types/react-dom": "^16.9.5",
81
81
  "@types/react-text-mask": "^5.4.6",
82
82
  "@types/react-transition-group": "^4.4.0",
83
- "axios": "0.21.1",
83
+ "axios": "0.21.2",
84
84
  "body-scroll-lock": "^3.1.5",
85
85
  "dayjs": "^1.10.7",
86
86
  "jwt-decode": "^2.2.0",
@@ -100,5 +100,5 @@
100
100
  "tailwindcss": "2.2.10",
101
101
  "typesafe-actions": "^5.1.0"
102
102
  },
103
- "gitHead": "cf5c2f24a088665630fd3830bb6758e316b2cbb4"
103
+ "gitHead": "c461d8695c7166c108a30e653445c8d542261f2e"
104
104
  }
@@ -19,12 +19,13 @@
19
19
  @apply absolute;
20
20
  @apply z-10;
21
21
  @apply w-full;
22
- @apply mt-4;
22
+ @apply mt-1;
23
+ @apply px-4;
24
+ @apply flex-wrap;
23
25
 
24
26
  @screen md {
25
27
  @apply items-start;
26
28
  @apply justify-start;
27
- @apply ml-4;
28
29
  }
29
30
  }
30
31
 
@@ -35,35 +36,6 @@
35
36
  @apply py-1;
36
37
  }
37
38
 
38
- .image-card__overlay {
39
- background: linear-gradient(
40
- 0deg,
41
- rgba(0, 0, 0, 0.9) 0%,
42
- rgba(0, 0, 0, 0.8) 10%,
43
- rgba(0, 0, 0, 0.7) 50%,
44
- rgba(0, 0, 0, 0.3) 90%,
45
- rgba(0, 0, 0, 0) 100%
46
- );
47
- @screen md {
48
- background: linear-gradient(
49
- 0deg,
50
- rgba(0, 0, 0, 0.9) 20%,
51
- rgba(0, 0, 0, 0.5) 50%,
52
- rgba(0, 0, 0, 0.2) 65%,
53
- rgba(0, 0, 0, 0.1) 70%,
54
- rgba(0, 0, 0, 0) 80%
55
- );
56
- }
57
- position: absolute;
58
- bottom: 0;
59
- left: 0;
60
- @apply w-full;
61
- height: 100%;
62
- @screen md {
63
- height: 100%;
64
- }
65
- }
66
-
67
39
  .image-card__placeholder {
68
40
  height: 300px;
69
41
  }
@@ -6,7 +6,7 @@ import { Tag } from "../text/Tag"
6
6
  import { ApplicationStatusType } from "../global/ApplicationStatusType"
7
7
  import { AppearanceStyleType } from "../global/AppearanceTypes"
8
8
  import { t } from "../helpers/translator"
9
- import { IconTypes } from "../icons/Icon"
9
+ import { Icon, IconFillColors, IconTypes } from "../icons/Icon"
10
10
 
11
11
  export interface StatusBarType {
12
12
  status?: ApplicationStatusType
@@ -16,16 +16,32 @@ export interface StatusBarType {
16
16
  iconType?: IconTypes
17
17
  }
18
18
 
19
+ export interface ImageTag {
20
+ text?: string
21
+ iconType?: IconTypes
22
+ iconColor?: string
23
+ }
24
+
19
25
  export interface ImageCardProps {
20
- imageUrl?: string
21
- subtitle?: string
22
- title: string
23
- href?: string
24
26
  description?: string
25
- tagLabel?: string
27
+ href?: string
28
+ imageUrl?: string
26
29
  statuses?: StatusBarType[]
30
+ tags?: ImageTag[]
27
31
  }
28
32
 
33
+ /**
34
+ * @component ImageCard
35
+ *
36
+ * A component that renders an image with optional status bars below it
37
+ *
38
+ * @prop description - A description of the image, used as alt text
39
+ * @prop href - A link, used to wrap the entire component
40
+ * @prop imageUrl - An image URL, used as a background image
41
+ * @prop statuses - A list of status indicators, an ApplicationStatus component is rendered for each item at the bottom of the card
42
+ * @prop tags - A list of image tags, a Tag component is rendered for each over the image
43
+ *
44
+ */
29
45
  const ImageCard = (props: ImageCardProps) => {
30
46
  const getStatuses = () => {
31
47
  return props.statuses?.map((status, index) => {
@@ -46,22 +62,31 @@ const ImageCard = (props: ImageCardProps) => {
46
62
 
47
63
  const image = (
48
64
  <div className="image-card__wrapper">
49
- {props.tagLabel && (
50
- <div className="image-card-tag__wrapper">
51
- <Tag styleType={AppearanceStyleType.warning}>{props.tagLabel}</Tag>
52
- </div>
53
- )}
65
+ <div className="image-card-tag__wrapper">
66
+ {props.tags?.map((tag, index) => {
67
+ return (
68
+ <React.Fragment key={index}>
69
+ <Tag styleType={AppearanceStyleType.warning} className={"mt-3 mr-2 ml-2"}>
70
+ {tag.iconType && (
71
+ <Icon
72
+ size={"medium"}
73
+ symbol={tag.iconType}
74
+ fill={tag.iconColor ?? IconFillColors.primary}
75
+ className={"mr-2"}
76
+ />
77
+ )}
78
+ {tag.text}
79
+ </Tag>
80
+ </React.Fragment>
81
+ )
82
+ })}
83
+ </div>
54
84
  <figure className="image-card">
55
85
  {props.imageUrl ? (
56
86
  <img src={props.imageUrl} alt={props.description || t("listings.buildingImageAltText")} />
57
87
  ) : (
58
88
  <div className={"image-card__placeholder"} />
59
89
  )}
60
- <div className={"image-card__overlay"} />
61
- <figcaption className="image-card__figcaption">
62
- <h2 className="image-card__title">{props.title}</h2>
63
- {props.subtitle && <p className="image-card__subtitle">{props.subtitle}</p>}
64
- </figcaption>
65
90
  </figure>
66
91
  {getStatuses()}
67
92
  </div>
@@ -139,3 +139,33 @@ h1.title {
139
139
  .icon-item {
140
140
  @apply block;
141
141
  }
142
+
143
+ .card-header {
144
+ @apply font-alt-sans;
145
+ @apply font-semibold;
146
+ @apply text-blue-700;
147
+ @apply text-3xl;
148
+ @apply mb-1;
149
+ }
150
+
151
+ .card-subheader {
152
+ @apply font-alt-sans;
153
+ @apply text-black;
154
+ @apply text-base;
155
+ @apply mb-3;
156
+ }
157
+
158
+ .table-header {
159
+ @apply font-alt-sans;
160
+ @apply font-semibold;
161
+ @apply text-gray-800;
162
+ @apply text-sm;
163
+ @apply mb-1;
164
+ }
165
+
166
+ .table-subheader {
167
+ @apply font-alt-sans;
168
+ @apply text-gray-750;
169
+ @apply text-sm;
170
+ @apply mb-3;
171
+ }
@@ -3,15 +3,26 @@ import * as React from "react"
3
3
  export interface HeadingProps {
4
4
  children?: React.ReactNode
5
5
  className?: string
6
- underline?: boolean
7
6
  priority?: number
7
+ style?: HeaderType
8
8
  }
9
+
10
+ export type HeaderType = keyof typeof HeaderStyleMap
11
+
12
+ const HeaderStyleMap = {
13
+ cardHeader: "card-header",
14
+ cardSubheader: "card-subheader",
15
+ tableHeader: "table-header",
16
+ tableSubheader: "table-subheader",
17
+ sidebarHeader: "text-caps-underline",
18
+ }
19
+
9
20
  const Heading = (props: HeadingProps) => {
10
- const Tag = `h${props.priority || 1}` as keyof JSX.IntrinsicElements
11
- let classNames = props.className || ""
12
- if (props.underline) {
13
- classNames += " text-caps-underline"
14
- }
21
+ const priority = props.priority && props.priority >= 1 && props.priority <= 6 ? props.priority : 1
22
+ const Tag = `h${priority}` as keyof JSX.IntrinsicElements
23
+ const classNames = `${props.style && HeaderStyleMap[props.style]} ${
24
+ props.className && props.className
25
+ }`
15
26
  return <Tag className={classNames}>{props.children}</Tag>
16
27
  }
17
28
 
@@ -54,7 +54,9 @@ const PreferencesList = (props: PreferencesListProps) => {
54
54
  <div className="preferences-list__links">
55
55
  {preference.links.map((link: ListPreferenceLink, linkIndex: number) => (
56
56
  <span key={linkIndex}>
57
- <a href={link.url}>{link.title}</a>
57
+ <a href={link.url} target="_blank">
58
+ {link.title}
59
+ </a>
58
60
  </span>
59
61
  ))}
60
62
  </div>
@@ -34,19 +34,17 @@
34
34
  }
35
35
  }
36
36
 
37
- .listings-row_title {
38
- @apply font-alt-sans;
39
- @apply font-semibold;
40
- @apply text-gray-900;
41
- @apply text-base;
42
- @apply mb-2;
37
+ .listings-row_table {
38
+ @apply mb-4;
43
39
  }
44
40
 
45
- .listings-row_subtitle {
46
- @apply font-alt-sans;
47
- @apply text-gray-800;
48
- @apply text-sm;
49
- @apply mb-3;
41
+ .listings-row_footer {
42
+ @apply flex;
43
+ @apply justify-end;
44
+ @apply w-full;
45
+ a:not(:first-child) {
46
+ @apply ml-1;
47
+ }
50
48
  }
51
49
 
52
50
  .listings-row_table {
@@ -1,70 +1,133 @@
1
1
  import * as React from "react"
2
- import { ImageCard, ImageCardProps } from "../../blocks/ImageCard"
2
+ import { ImageCard, ImageCardProps, ImageTag } from "../../blocks/ImageCard"
3
3
  import { LinkButton } from "../../actions/LinkButton"
4
4
  import { StackedTable, StackedTableProps } from "../../tables/StackedTable"
5
-
6
- import { t } from "../../helpers/translator"
7
- import "./ListingCard.scss"
8
5
  import { StandardTable, StandardTableProps } from "../../tables/StandardTable"
6
+ import { Heading, HeaderType } from "../../headers/Heading"
7
+ import { Tag } from "../../text/Tag"
8
+ import { AppearanceStyleType } from "../../global/AppearanceTypes"
9
+ import { Icon, IconFillColors } from "../../icons/Icon"
10
+ import "./ListingCard.scss"
9
11
 
10
12
  interface ListingCardTableProps extends StandardTableProps, StackedTableProps {}
11
13
 
12
- export interface ListingCardHeaderProps {
13
- tableHeader?: string
14
- tableHeaderClass?: string
15
- tableSubHeader?: string
16
- tableSubHeaderClass?: string
17
- stackedTable?: boolean
14
+ export interface CardHeader {
15
+ customClass?: string
16
+ text: string
17
+ }
18
+
19
+ export interface FooterButton {
20
+ href: string
21
+ text: string
22
+ }
23
+
24
+ export interface ListingCardContentProps {
25
+ contentHeader?: CardHeader
26
+ contentSubheader?: CardHeader
27
+ tableHeader?: CardHeader
28
+ tableSubheader?: CardHeader
18
29
  }
19
30
  export interface ListingCardProps {
20
- imageCardProps: ImageCardProps
31
+ cardTags?: ImageTag[]
21
32
  children?: React.ReactElement
22
- seeDetailsLink?: string
23
- tableHeaderProps?: ListingCardHeaderProps
33
+ contentProps?: ListingCardContentProps
34
+ footerButtons?: FooterButton[]
35
+ footerContainerClass?: string
36
+ footerContent?: React.ReactNode
37
+ imageCardProps: ImageCardProps
38
+ stackedTable?: boolean
24
39
  tableProps?: ListingCardTableProps
25
- detailsLinkClass?: string
26
40
  }
27
41
 
42
+ /**
43
+ * @component ListingCard
44
+ *
45
+ * A component that renders an image with optional status bars below it,
46
+ * and a content section associated with the image which can include titles, a table, and custom content
47
+ *
48
+ * @prop cardTags -A list of tags to be rendered below the content header, a Tag component is rendered for each
49
+ * @prop children - Custom content rendered in the content section above the table
50
+ * @prop footerButtons - A list of buttons to render in the footer of the content section
51
+ * @prop footerContent - Custom content rendered below the content table
52
+ * @prop footerContainerClass - A class name applied to the footer container of the content section
53
+ * @prop imageCardProps - Prop interface for the ImageCard component
54
+ * @prop stackedTable - Toggles on the StackedTable component in place of the default StandardTable component - they are functionally equivalent with differing UIs
55
+ * @prop contentProps - An object containing fields that render optional headers above the content section's table
56
+ * @prop tableProps - Prop interface for the StandardTable and StackedTable components
57
+ *
58
+ */
28
59
  const ListingCard = (props: ListingCardProps) => {
29
- const { imageCardProps, tableProps, detailsLinkClass, tableHeaderProps, children } = props
60
+ const {
61
+ cardTags,
62
+ children,
63
+ footerButtons,
64
+ footerContent,
65
+ footerContainerClass,
66
+ imageCardProps,
67
+ stackedTable,
68
+ contentProps,
69
+ tableProps,
70
+ } = props
30
71
 
31
- const tableHeader = () => {
32
- return (
33
- <h3
34
- className={`listings-row_title ${
35
- tableHeaderProps?.tableHeaderClass && tableHeaderProps?.tableHeaderClass
36
- }`}
37
- >
38
- {tableHeaderProps?.tableHeader}
39
- </h3>
40
- )
72
+ const getHeader = (
73
+ header: CardHeader | undefined,
74
+ priority: number,
75
+ style?: HeaderType,
76
+ customClass?: string
77
+ ) => {
78
+ if (header && header.text) {
79
+ return (
80
+ <Heading priority={priority} style={style} className={customClass}>
81
+ {header.text}
82
+ </Heading>
83
+ )
84
+ } else {
85
+ return <></>
86
+ }
41
87
  }
42
88
 
43
- const tableSubHeader = () => {
89
+ const getContentHeader = () => {
44
90
  return (
45
- <h4
46
- className={`listings-row_subtitle ${
47
- tableHeaderProps?.tableSubHeaderClass && tableHeaderProps?.tableSubHeaderClass
48
- }`}
49
- >
50
- {tableHeaderProps?.tableSubHeader}
51
- </h4>
91
+ <>
92
+ {getHeader(contentProps?.contentHeader, 2, "cardHeader", "order-1")}
93
+ {getHeader(contentProps?.contentSubheader, 3, "cardSubheader", "order-2")}
94
+ {cardTags && cardTags?.length > 0 && (
95
+ <div className={"inline-flex flex-wrap justify-start w-full"}>
96
+ {cardTags?.map((cardTag, index) => {
97
+ return (
98
+ <Tag styleType={AppearanceStyleType.warning} className={"mr-2 mb-2"} key={index}>
99
+ {cardTag.iconType && (
100
+ <Icon
101
+ size={"medium"}
102
+ symbol={cardTag.iconType}
103
+ fill={cardTag.iconColor ?? IconFillColors.primary}
104
+ className={"mr-2"}
105
+ />
106
+ )}
107
+ {cardTag.text}
108
+ </Tag>
109
+ )
110
+ })}
111
+ </div>
112
+ )}
113
+ </>
52
114
  )
53
115
  }
54
116
 
55
- return (
56
- <article className="listings-row" data-test-id={"listing-card-component"}>
57
- <div className="listings-row_figure">
58
- <ImageCard {...imageCardProps} />
59
- </div>
60
- <div className="listings-row_content">
61
- {tableHeaderProps?.tableHeader && tableHeader()}
62
- {tableHeaderProps?.tableSubHeader && tableSubHeader()}
117
+ const getContent = () => {
118
+ return (
119
+ <>
63
120
  <div className="listings-row_table">
121
+ {(contentProps?.tableHeader?.text || contentProps?.tableSubheader?.text) &&
122
+ (contentProps.contentHeader?.text || contentProps?.contentSubheader?.text) && (
123
+ <hr className={"mb-2"} />
124
+ )}
125
+ {getHeader(contentProps?.tableHeader, 4, "tableHeader")}
126
+ {getHeader(contentProps?.tableSubheader, 5, "tableSubheader")}
64
127
  {children && children}
65
128
  {tableProps && (tableProps.data || tableProps.stackedData) && (
66
129
  <>
67
- {tableHeaderProps?.stackedTable ? (
130
+ {stackedTable ? (
68
131
  <StackedTable {...(tableProps as StackedTableProps)} />
69
132
  ) : (
70
133
  <StandardTable {...(tableProps as StandardTableProps)} />
@@ -72,11 +135,32 @@ const ListingCard = (props: ListingCardProps) => {
72
135
  </>
73
136
  )}
74
137
  </div>
75
- {props.seeDetailsLink && (
76
- <LinkButton className={detailsLinkClass} href={props.seeDetailsLink}>
77
- {t("t.seeDetails")}
78
- </LinkButton>
79
- )}
138
+ <div className={"flex flex-col"}>
139
+ {footerContent && footerContent}
140
+ {footerButtons && footerButtons?.length > 0 && (
141
+ <div className={footerContainerClass ?? "listings-row_footer"}>
142
+ {footerButtons?.map((footerButton, index) => {
143
+ return (
144
+ <LinkButton href={footerButton.href} key={index}>
145
+ {footerButton.text}
146
+ </LinkButton>
147
+ )
148
+ })}
149
+ </div>
150
+ )}
151
+ </div>
152
+ </>
153
+ )
154
+ }
155
+
156
+ return (
157
+ <article className="listings-row" data-test-id={"listing-card-component"}>
158
+ <div className="listings-row_figure">
159
+ <ImageCard {...imageCardProps} />
160
+ </div>
161
+ <div className="listings-row_content">
162
+ <div>{getContentHeader()}</div>
163
+ {getContent()}
80
164
  </div>
81
165
  </article>
82
166
  )