@bloom-housing/ui-components 6.0.1-alpha.1 → 6.0.1-alpha.2
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 +26 -0
- package/index.ts +2 -0
- package/package.json +2 -2
- package/src/blocks/Card.docs.mdx +60 -0
- package/src/blocks/Card.scss +65 -0
- package/src/blocks/Card.tsx +74 -0
- package/src/headers/Heading.tsx +5 -4
- package/src/headers/HeadingGroup.docs.mdx +25 -0
- package/src/headers/HeadingGroup.scss +23 -0
- package/src/headers/HeadingGroup.tsx +31 -0
- package/src/page_components/listing/ListingCard.tsx +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
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
|
+
## [6.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@6.0.1-alpha.1...@bloom-housing/ui-components@6.0.1-alpha.2) (2022-09-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
* 2938/card component (#3043) ([7a4a148](https://github.com/bloom-housing/bloom/commit/7a4a14891b4c8057f27a9f95e87a00d1d7a58be8)), closes [#3043](https://github.com/bloom-housing/bloom/issues/3043)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### BREAKING CHANGES
|
|
13
|
+
|
|
14
|
+
* Renamed exported interfaces in ListingCard so as to not conflict with Card and other components
|
|
15
|
+
|
|
16
|
+
* Fix code style issues with Prettier
|
|
17
|
+
|
|
18
|
+
* feat: split HeadingGroup out to its own separate component
|
|
19
|
+
|
|
20
|
+
* fix: simplify stories and styling for Card and HeadingGroup
|
|
21
|
+
|
|
22
|
+
* remove: prototype "slotted" card component
|
|
23
|
+
|
|
24
|
+
* test: fix the incorrect component name in Card
|
|
25
|
+
|
|
26
|
+
Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
6
32
|
## [6.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@6.0.1-alpha.0...@bloom-housing/ui-components@6.0.1-alpha.1) (2022-09-28)
|
|
7
33
|
|
|
8
34
|
|
package/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./src/actions/Video"
|
|
|
10
10
|
/* Blocks */
|
|
11
11
|
export * from "./src/blocks/ActionBlock"
|
|
12
12
|
export * from "./src/blocks/StatusItem"
|
|
13
|
+
export * from "./src/blocks/Card"
|
|
13
14
|
export * from "./src/blocks/DashBlock"
|
|
14
15
|
export * from "./src/blocks/DashBlocks"
|
|
15
16
|
export * from "./src/blocks/FormCard"
|
|
@@ -55,6 +56,7 @@ export * from "./src/headers/PageHeader"
|
|
|
55
56
|
export * from "./src/headers/SiteHeader"
|
|
56
57
|
export * from "./src/headers/StepHeader"
|
|
57
58
|
export * from "./src/headers/Heading"
|
|
59
|
+
export * from "./src/headers/HeadingGroup"
|
|
58
60
|
|
|
59
61
|
/* Helpers */
|
|
60
62
|
export * from "./src/helpers/capitalize"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloom-housing/ui-components",
|
|
3
|
-
"version": "6.0.1-alpha.
|
|
3
|
+
"version": "6.0.1-alpha.2",
|
|
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",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"ts-jest": "^26.4.1",
|
|
111
111
|
"typesafe-actions": "^5.1.0"
|
|
112
112
|
},
|
|
113
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "2a2bd0fb61d7643c31d4a200b96a78d401bd0590"
|
|
114
114
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
|
|
2
|
+
import { Swatch } from "../prototypes/Swatch"
|
|
3
|
+
import { Card } from "./Card"
|
|
4
|
+
|
|
5
|
+
# Card
|
|
6
|
+
|
|
7
|
+
<Canvas>
|
|
8
|
+
<Story id="blocks-card--text-content" />
|
|
9
|
+
</Canvas>
|
|
10
|
+
|
|
11
|
+
The 2nd-generation Card component is intended to serve as a flexible content container with a header, optional content sections, and a footer. Over time our intention is to "port" some other card types and content blocks in the system to use Card as their underlying structure.
|
|
12
|
+
|
|
13
|
+
Styles within the Card node tree are intended to set up sensible defaults without requiring the consumer to configure bespoke styling as they author a card. For example, rule lines will be drawn automatically between the various subcomponents within a card, and margin/padding will be set according to top-level variables.
|
|
14
|
+
|
|
15
|
+
You can author a Card using subcomponents simply by referencing them via a single Card import. For example, `<Card.Header>`, `<Card.Footer>` etc. In the case where you want to create a new type of card, you can import the subcomponents directly as `<CardHeader>`, `<CardFooter>`, etc. and place them in any container. It's still recommended you include the `.card` class on your custom container so you inherit a base set of CSS properties.
|
|
16
|
+
|
|
17
|
+
Note that the `<Card.Footer>` subcomponent doesn't inherently add rules or padding so that you can use other components with self-contained styling such as `<ButtonGroup>`. However, you can add `<Card.Section>` within `<Card.Footer>` to add rules and padding.
|
|
18
|
+
|
|
19
|
+
The `<Card.Header>` subcomponent will typically be paired with a child `<HeadingGroup>` for a title + subtitle grouping, but that's not a requirement.
|
|
20
|
+
|
|
21
|
+
You can browse through the various stories for different examples of how you'd pattern a card with various subcomponents. Many of the visual aspects of a Card can be customized using the CSS variables listed below. For example, this "Detroit style" example:
|
|
22
|
+
|
|
23
|
+
<Canvas>
|
|
24
|
+
<Story id="blocks-card--detroit-style" />
|
|
25
|
+
</Canvas>
|
|
26
|
+
|
|
27
|
+
## Card Properties
|
|
28
|
+
|
|
29
|
+
<ArgsTable of={Card} />
|
|
30
|
+
|
|
31
|
+
## Card.Header Properties
|
|
32
|
+
|
|
33
|
+
<ArgsTable of={Card.Header} />
|
|
34
|
+
|
|
35
|
+
## Card.Section Properties
|
|
36
|
+
|
|
37
|
+
<ArgsTable of={Card.Section} />
|
|
38
|
+
|
|
39
|
+
## Card.Footer Properties
|
|
40
|
+
|
|
41
|
+
<ArgsTable of={Card.Footer} />
|
|
42
|
+
|
|
43
|
+
## Theming Variables
|
|
44
|
+
|
|
45
|
+
You can apply CSS variables to the `.card` selector to customize the appearance of both the component and various subcomponents.
|
|
46
|
+
|
|
47
|
+
| Name | Type | Description | Default |
|
|
48
|
+
| --------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------ |
|
|
49
|
+
| `--background-color` | <Swatch colorVar="--bloom-color-white" /> | Background color of the card | `--bloom-color-white` |
|
|
50
|
+
| `--border-radius` | Size | Border radius of the card | `--bloom-rounded-lg` |
|
|
51
|
+
| `--border-width` | Size | Border width of the card (and rules by default) | `--bloom-border-1` |
|
|
52
|
+
| `--border-color` | <Swatch colorVar="--bloom-color-gray-450" /> | Border color of the card (and rules by default) | `--bloom-color-gray-450` |
|
|
53
|
+
| `--rule-width` | Size | Width of the inset rules | `--border-width` |
|
|
54
|
+
| `--rule-color` | <Swatch colorVar="--bloom-color-gray-450" /> | Color of the inset rules | `--border-color` |
|
|
55
|
+
| `--content-padding-block` | Size | The space above and below content sections | `--bloom-s8` |
|
|
56
|
+
| `--content-margin-inline-desktop` | Size | The space to the left and right of content sections for desktop-size screens | `--bloom-s16` |
|
|
57
|
+
| `--content-margin-inline-mobile` | Size | The space to the left and right of content sections for mobile-size screens | `--bloom-s4` |
|
|
58
|
+
| `--header-padding-block` | Size | The space above and below the header | `--bloom-s8` |
|
|
59
|
+
| `--header-margin-inline-desktop` | Size | The space to the left and right of the header for desktop-size screens | `--bloom-s16` |
|
|
60
|
+
| `--header-margin-inline-mobile` | Size | The space to the left and right of the header for mobile-size screens | `--bloom-s4` |
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
.card {
|
|
2
|
+
/* Component Variables */
|
|
3
|
+
--background-color: var(--bloom-color-white);
|
|
4
|
+
--border-radius: var(--bloom-rounded-lg);
|
|
5
|
+
--border-width: var(--bloom-border-1);
|
|
6
|
+
--border-color: var(--bloom-color-gray-450);
|
|
7
|
+
--rule-width: var(--border-width);
|
|
8
|
+
--rule-color: var(--border-color);
|
|
9
|
+
|
|
10
|
+
--content-padding-block: var(--bloom-s8);
|
|
11
|
+
--content-margin-inline-desktop: var(--bloom-s16);
|
|
12
|
+
--content-margin-inline-mobile: var(--bloom-s4);
|
|
13
|
+
--content-margin-inline: var(--content-margin-inline-desktop);
|
|
14
|
+
|
|
15
|
+
--header-padding-block: var(--bloom-s8) var(--bloom-s3);
|
|
16
|
+
--header-margin-inline-desktop: var(--content-margin-inline-desktop);
|
|
17
|
+
--header-margin-inline-mobile: var(--content-margin-inline-mobile);
|
|
18
|
+
--header-margin-inline: var(--header-margin-inline-desktop);
|
|
19
|
+
|
|
20
|
+
@media (max-width: $screen-sm) {
|
|
21
|
+
--content-margin-inline: var(--content-margin-inline-mobile);
|
|
22
|
+
--header-margin-inline: var(--header-margin-inline-mobile);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Default Styles */
|
|
26
|
+
background-color: var(--background-color);
|
|
27
|
+
border: var(--border-width) solid var(--border-color);
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
border-radius: var(--border-radius);
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.card__header {
|
|
35
|
+
padding-block: var(--header-padding-block);
|
|
36
|
+
margin-inline: var(--header-margin-inline);
|
|
37
|
+
border-bottom: var(--rule-width) solid var(--rule-color);
|
|
38
|
+
|
|
39
|
+
&.is-flex {
|
|
40
|
+
display: flex;
|
|
41
|
+
justify-content: space-between;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--bloom-s7);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.card__header + .card__footer {
|
|
48
|
+
margin-block-start: var(--content-padding-block);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.card > *:nth-child(n + 3),
|
|
52
|
+
.card__footer > .card__section {
|
|
53
|
+
border-top: var(--rule-width) solid var(--rule-color);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.card__section {
|
|
57
|
+
padding-block: var(--content-padding-block);
|
|
58
|
+
margin-inline: var(--content-margin-inline);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Using the double-class specificity hack, proceed with caution: */
|
|
62
|
+
.card__footer.card__footer,
|
|
63
|
+
.card__footer.card__footer > :not(.card__section) + .card__section {
|
|
64
|
+
border-top: none;
|
|
65
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import "./Card.scss"
|
|
3
|
+
|
|
4
|
+
export interface CardHeaderProps {
|
|
5
|
+
/** An additional element(s) you can add to the side of the main child element(s) */
|
|
6
|
+
suffix?: React.ReactNode
|
|
7
|
+
/** Additional class name */
|
|
8
|
+
className?: string
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const CardHeader = (props: CardHeaderProps) => {
|
|
13
|
+
const classNames = ["card__header"]
|
|
14
|
+
if (props.suffix) classNames.push("is-flex")
|
|
15
|
+
if (props.className) classNames.push(props.className)
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<header className={classNames.join(" ")}>
|
|
19
|
+
{props.children}
|
|
20
|
+
{props.suffix}
|
|
21
|
+
</header>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CardSectionProps {
|
|
26
|
+
/**
|
|
27
|
+
* Whether to center the text within the section
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
centered?: boolean
|
|
31
|
+
/** Additional class name */
|
|
32
|
+
className?: string
|
|
33
|
+
children: React.ReactNode
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const CardSection = (props: CardSectionProps) => {
|
|
37
|
+
const classNames = ["card__section"]
|
|
38
|
+
if (props.centered) classNames.push("text-center")
|
|
39
|
+
if (props.className) classNames.push(props.className)
|
|
40
|
+
|
|
41
|
+
return <div className={classNames.join(" ")}>{props.children}</div>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface CardFooterProps {
|
|
45
|
+
/** Additional class name */
|
|
46
|
+
className?: string
|
|
47
|
+
children: React.ReactNode
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const CardFooter = (props: CardFooterProps) => {
|
|
51
|
+
const classNames = ["card__footer"]
|
|
52
|
+
if (props.className) classNames.push(props.className)
|
|
53
|
+
|
|
54
|
+
return <footer className={classNames.join(" ")}>{props.children}</footer>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CardProps {
|
|
58
|
+
/** Additional class name */
|
|
59
|
+
className?: string
|
|
60
|
+
children: React.ReactNode
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const Card = (props: CardProps) => {
|
|
64
|
+
const classNames = ["card"]
|
|
65
|
+
if (props.className) classNames.push(props.className)
|
|
66
|
+
|
|
67
|
+
return <article className={classNames.join(" ")}>{props.children}</article>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Card.Header = CardHeader
|
|
71
|
+
Card.Section = CardSection
|
|
72
|
+
Card.Footer = CardFooter
|
|
73
|
+
|
|
74
|
+
export { Card as default, Card, CardHeader, CardSection, CardFooter }
|
package/src/headers/Heading.tsx
CHANGED
|
@@ -22,10 +22,11 @@ const HeaderStyleMap = {
|
|
|
22
22
|
const Heading = (props: HeadingProps) => {
|
|
23
23
|
const priority = props.priority && props.priority >= 1 && props.priority <= 6 ? props.priority : 1
|
|
24
24
|
const Tag = `h${priority}` as keyof JSX.IntrinsicElements
|
|
25
|
-
const classNames =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const classNames = []
|
|
26
|
+
if (props.style) classNames.push(HeaderStyleMap[props.style])
|
|
27
|
+
if (props.className) classNames.push(props.className)
|
|
28
|
+
|
|
29
|
+
return <Tag className={classNames.join(" ")}>{props.children}</Tag>
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export { Heading as default, Heading }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
|
|
2
|
+
import { HeadingGroup } from "./HeadingGroup"
|
|
3
|
+
|
|
4
|
+
# Heading Group
|
|
5
|
+
|
|
6
|
+
<Canvas>
|
|
7
|
+
<Story id="headers-heading-group--with-content" />
|
|
8
|
+
</Canvas>
|
|
9
|
+
|
|
10
|
+
The HeadingGroup component provides a heading tag (h1-h6) combined with a paragraph tag as a subheading in an aria-minded fashion. Good for use in Card header components and other headers.
|
|
11
|
+
|
|
12
|
+
## Heading Group Properties
|
|
13
|
+
|
|
14
|
+
<ArgsTable of={HeadingGroup} />
|
|
15
|
+
|
|
16
|
+
## Theming Variables
|
|
17
|
+
|
|
18
|
+
You can apply CSS variables to the `.heading-group` selector to customize the appearance of the component.
|
|
19
|
+
|
|
20
|
+
| Name | Type | Description | Default |
|
|
21
|
+
| --------------------- | ----- | --------------------------------------------------- | ------------ |
|
|
22
|
+
| `--heading-margin` | Size | Vertical space added around the top heading, if any | `0rem` |
|
|
23
|
+
| `--subheading-margin` | Size | The space between the heading and subheading | `--bloom-s3` |
|
|
24
|
+
| `--heading-color` | Color | Override to set a specific heading text color | `inherit` |
|
|
25
|
+
| `--subheading-color` | Color | Override to set a specific subheading text color | `inherit` |
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.heading-group {
|
|
2
|
+
/* Component Variables */
|
|
3
|
+
--heading-margin: 0rem;
|
|
4
|
+
--subheading-margin: var(--bloom-s3);
|
|
5
|
+
--heading-color: inherit;
|
|
6
|
+
--subheading-color: inherit;
|
|
7
|
+
|
|
8
|
+
h1,
|
|
9
|
+
h2,
|
|
10
|
+
h3,
|
|
11
|
+
h4,
|
|
12
|
+
h5,
|
|
13
|
+
h6 {
|
|
14
|
+
margin-block: var(--heading-margin);
|
|
15
|
+
color: var(--heading-color);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
p {
|
|
19
|
+
margin-block: var(--subheading-margin);
|
|
20
|
+
font-size: var(--bloom-font-size-base);
|
|
21
|
+
color: var(--subheading-color);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Heading } from "./Heading"
|
|
3
|
+
import "./HeadingGroup.scss"
|
|
4
|
+
|
|
5
|
+
export interface HeadingGroupProps {
|
|
6
|
+
/** A string or element to display in an `h2` tag (overridable via `headingPriority`) */
|
|
7
|
+
heading: React.ReactNode
|
|
8
|
+
/** A string or element to display in an `p` tag (using `aria-roledescription="subtitle"`) */
|
|
9
|
+
subheading: React.ReactNode
|
|
10
|
+
/**
|
|
11
|
+
* The heading level (1 through 6)
|
|
12
|
+
* @default 2
|
|
13
|
+
*/
|
|
14
|
+
headingPriority?: number
|
|
15
|
+
/** Additional class name for the whole group */
|
|
16
|
+
className?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const HeadingGroup = (props: HeadingGroupProps) => {
|
|
20
|
+
const classNames = ["heading-group"]
|
|
21
|
+
if (props.className) classNames.push(props.className)
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<hgroup className={classNames.join(" ")} role="group">
|
|
25
|
+
<Heading priority={props.headingPriority ?? 2}>{props.heading}</Heading>
|
|
26
|
+
<p aria-roledescription="subtitle">{props.subheading}</p>
|
|
27
|
+
</hgroup>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { HeadingGroup as default, HeadingGroup }
|
|
@@ -12,7 +12,7 @@ import { NavigationContext } from "../../config/NavigationContext"
|
|
|
12
12
|
|
|
13
13
|
interface ListingCardTableProps extends StandardTableProps, StackedTableProps {}
|
|
14
14
|
|
|
15
|
-
export interface
|
|
15
|
+
export interface ListingCardHeader {
|
|
16
16
|
content: string | React.ReactNode
|
|
17
17
|
href?: string
|
|
18
18
|
customClass?: string
|
|
@@ -20,17 +20,17 @@ export interface CardHeader {
|
|
|
20
20
|
isPillType?: boolean
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface
|
|
23
|
+
export interface ListingFooterButton {
|
|
24
24
|
href: string
|
|
25
25
|
text: string
|
|
26
26
|
ariaHidden?: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface ListingCardContentProps {
|
|
30
|
-
contentHeader?:
|
|
31
|
-
contentSubheader?:
|
|
32
|
-
tableHeader?:
|
|
33
|
-
tableSubheader?:
|
|
30
|
+
contentHeader?: ListingCardHeader
|
|
31
|
+
contentSubheader?: ListingCardHeader
|
|
32
|
+
tableHeader?: ListingCardHeader
|
|
33
|
+
tableSubheader?: ListingCardHeader
|
|
34
34
|
}
|
|
35
35
|
export interface ListingCardProps {
|
|
36
36
|
/** A list of tags to be rendered below the content header, a Tag component is rendered for each */
|
|
@@ -40,7 +40,7 @@ export interface ListingCardProps {
|
|
|
40
40
|
/** An object containing fields that render optional headers above the content section's table */
|
|
41
41
|
contentProps?: ListingCardContentProps
|
|
42
42
|
/** A list of buttons to render in the footer of the content section */
|
|
43
|
-
footerButtons?:
|
|
43
|
+
footerButtons?: ListingFooterButton[]
|
|
44
44
|
/** A class name applied to the footer container of the content section */
|
|
45
45
|
footerContainerClass?: string
|
|
46
46
|
/** Custom content rendered below the content table */
|
|
@@ -74,7 +74,7 @@ const ListingCard = (props: ListingCardProps) => {
|
|
|
74
74
|
const { LinkComponent } = useContext(NavigationContext)
|
|
75
75
|
|
|
76
76
|
const getHeader = (
|
|
77
|
-
header:
|
|
77
|
+
header: ListingCardHeader | undefined,
|
|
78
78
|
priority: number,
|
|
79
79
|
styleType?: HeaderType,
|
|
80
80
|
customClass?: string
|