@bloom-housing/ui-components 5.0.1-alpha.16 → 5.0.1-alpha.19
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 +30 -0
- package/package.json +2 -2
- package/src/forms/Field.tsx +1 -1
- package/src/forms/Textarea.tsx +2 -0
- package/src/global/blocks.scss +22 -0
- package/src/global/tokens/screens.scss +1 -0
- package/src/navigation/SideNav.docs.mdx +34 -0
- package/src/navigation/SideNav.scss +58 -24
- package/src/navigation/SideNav.tsx +44 -9
- package/src/notifications/SiteAlert.tsx +25 -1
- package/tailwind.config.js +1 -0
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
|
+
## [5.0.1-alpha.19](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.18...@bloom-housing/ui-components@5.0.1-alpha.19) (2022-07-19)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @bloom-housing/ui-components
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [5.0.1-alpha.18](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.17...@bloom-housing/ui-components@5.0.1-alpha.18) (2022-07-19)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* visuals for add preference, preference option drawers ([#2877](https://github.com/bloom-housing/bloom/issues/2877)) ([8611034](https://github.com/bloom-housing/bloom/commit/8611034845b45ce4d4e4eb44e790ac2adec0ba94))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [5.0.1-alpha.17](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.16...@bloom-housing/ui-components@5.0.1-alpha.17) (2022-07-15)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* creating users as admin ([#2856](https://github.com/bloom-housing/bloom/issues/2856)) ([dd946d1](https://github.com/bloom-housing/bloom/commit/dd946d1777b4678e89832da527768180f474d129))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
6
36
|
## [5.0.1-alpha.16](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.15...@bloom-housing/ui-components@5.0.1-alpha.16) (2022-07-13)
|
|
7
37
|
|
|
8
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloom-housing/ui-components",
|
|
3
|
-
"version": "5.0.1-alpha.
|
|
3
|
+
"version": "5.0.1-alpha.19",
|
|
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",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"ts-jest": "^26.4.1",
|
|
109
109
|
"typesafe-actions": "^5.1.0"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "416b300b573c622ffa776199bf9bd05ea7341d5f"
|
|
112
112
|
}
|
package/src/forms/Field.tsx
CHANGED
|
@@ -74,7 +74,7 @@ const Field = (props: FieldProps) => {
|
|
|
74
74
|
if (props.caps) labelClasses.push("field-label--caps")
|
|
75
75
|
if (props.primary) labelClasses.push("text-primary")
|
|
76
76
|
if (props.readerOnly) labelClasses.push("sr-only")
|
|
77
|
-
if (props.type === "
|
|
77
|
+
if (props.type === "radio") {
|
|
78
78
|
labelClasses.push("font-semibold")
|
|
79
79
|
}
|
|
80
80
|
|
package/src/forms/Textarea.tsx
CHANGED
|
@@ -23,6 +23,7 @@ export interface TextareaProps {
|
|
|
23
23
|
wrap?: WrapOptions
|
|
24
24
|
readerOnly?: boolean
|
|
25
25
|
inputProps?: Record<string, unknown>
|
|
26
|
+
dataTestId?: string
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export const Textarea = (props: TextareaProps) => {
|
|
@@ -56,6 +57,7 @@ export const Textarea = (props: TextareaProps) => {
|
|
|
56
57
|
wrap={props.wrap ?? "soft"}
|
|
57
58
|
title={props.label}
|
|
58
59
|
{...inputProps}
|
|
60
|
+
data-test-id={props.dataTestId}
|
|
59
61
|
/>
|
|
60
62
|
{props.note && <p className="field-note font-normal mb-2">{props.note}</p>}
|
|
61
63
|
{props.errorMessage && <span className="textarea-error-message">{props.errorMessage}</span>}
|
package/src/global/blocks.scss
CHANGED
|
@@ -133,3 +133,25 @@ $shadow-left-slight: -3px 0px 3px -1px rgba(0, 0, 0, 0.1);
|
|
|
133
133
|
@apply block;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
.sidebar-detail-layout {
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
gap: var(--bloom-s6);
|
|
141
|
+
margin-inline: auto;
|
|
142
|
+
max-width: var(--bloom-screen-2xl);
|
|
143
|
+
|
|
144
|
+
@media (min-width: 1000px) {
|
|
145
|
+
flex-direction: row;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
& > div {
|
|
149
|
+
flex-grow: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
& > aside {
|
|
153
|
+
@media (min-width: 1000px) {
|
|
154
|
+
width: var(--bloom-s72);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Canvas, Story, ArgsTable } from "@storybook/addon-docs"
|
|
2
|
+
import { Swatch } from "../prototypes/Swatch"
|
|
3
|
+
import { SideNav } from "./SideNav"
|
|
4
|
+
|
|
5
|
+
# Side Navigation
|
|
6
|
+
|
|
7
|
+
The side nav component renders a list of navigation links, any of which can optionally have children links for a hierarchical list.
|
|
8
|
+
|
|
9
|
+
<Canvas>
|
|
10
|
+
<Story id="navigation-side-nav--current" />
|
|
11
|
+
</Canvas>
|
|
12
|
+
|
|
13
|
+
<br />
|
|
14
|
+
<br />
|
|
15
|
+
|
|
16
|
+
## Component Properties
|
|
17
|
+
|
|
18
|
+
<ArgsTable of={SideNav} />
|
|
19
|
+
|
|
20
|
+
## Theming Variables
|
|
21
|
+
|
|
22
|
+
You can apply CSS variables to the `.side-nav` selector to customize the appearance of the component.
|
|
23
|
+
|
|
24
|
+
| Name | Type | Description | Default |
|
|
25
|
+
| --------------------------- | --------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
26
|
+
| `--border` | Border | Border of the nav | `solid var(--bloom-border-1) var(--bloom-color-gray-450)` |
|
|
27
|
+
| `--border-radius` | Size | The border radius of the nav | `--bloom-rounded-lg` |
|
|
28
|
+
| `--current-padding-inline` | Size | The space on either side of a link | `--bloom-s6` |
|
|
29
|
+
| `--current-padding-block` | Size | The space above and below a link | `--bloom-s4` |
|
|
30
|
+
| `--link-color` | <Swatch colorVar="--bloom-color-gray-700" /> | Default color of links | `--bloom-color-gray-700` |
|
|
31
|
+
| `--current-selection-color` | <Swatch colorVar="--bloom-color-gray-900" /> | Color of the current link and parent of the current link if applicable | `--bloom-color-gray-900` |
|
|
32
|
+
| `--selection-parent-accent` | <Swatch colorVar="--bloom-color-gray-450" /> | Color of the current link's parent's border accent | `--bloom-color-gray-900` |
|
|
33
|
+
| `--hover-link-color` | <Swatch colorVar="--bloom-color-primary" /> | Link text color when you hover over it or tab to it | `--bloom-color-primary` |
|
|
34
|
+
| `--hover-background-color` | <Swatch colorVar="--bloom-color-primary-lighter" /> | Link background color when you hover over it or tab to it | `--bloom-color-primary-lighter` |
|
|
@@ -1,38 +1,72 @@
|
|
|
1
1
|
.side-nav {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
--border: solid var(--bloom-border-1) var(--bloom-color-gray-450);
|
|
3
|
+
--border-radius: var(--bloom-rounded-lg);
|
|
4
|
+
--current-padding-inline: var(--bloom-s6);
|
|
5
|
+
--current-padding-block: var(--bloom-s4);
|
|
6
|
+
--link-color: var(--bloom-color-gray-700);
|
|
7
|
+
--current-selection-color: var(--bloom-color-gray-900);
|
|
8
|
+
--selection-parent-accent: var(--bloom-color-gray-450);
|
|
9
|
+
--hover-link-color: var(--bloom-color-primary);
|
|
10
|
+
--hover-background-color: var(--bloom-color-primary-lighter);
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@apply rounded-t-lg;
|
|
8
|
-
}
|
|
12
|
+
border: var(--border);
|
|
13
|
+
border-radius: var(--border-radius);
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
& > ul {
|
|
16
|
+
& > li:first-child > a {
|
|
17
|
+
border-top-right-radius: var(--border-radius);
|
|
18
|
+
border-top-left-radius: var(--border-radius);
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
box-shadow: inset 3px 0px 0px 0px $tailwind-primary;
|
|
18
|
-
@apply block;
|
|
19
|
-
@apply px-4;
|
|
20
|
-
@apply py-2;
|
|
21
|
+
& > li:last-child > a {
|
|
22
|
+
border-bottom-right-radius: var(--border-radius);
|
|
23
|
+
border-bottom-left-radius: var(--border-radius);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
& > li > ul > li:first-child {
|
|
27
|
+
border-top: var(--border);
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
li:not(:last-child) {
|
|
32
|
+
border-bottom: var(--border);
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
a {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
display: flex;
|
|
37
|
+
justify-content: space-between;
|
|
38
|
+
color: var(--link-color);
|
|
39
|
+
padding-inline: var(--current-padding-inline);
|
|
40
|
+
padding-block: var(--current-padding-block);
|
|
41
|
+
|
|
42
|
+
&[aria-current] {
|
|
43
|
+
color: var(--current-selection-color);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&[aria-current]:not(:focus) {
|
|
47
|
+
box-shadow: inset 3px 0px 0px 0px var(--hover-link-color);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.has-current-child {
|
|
51
|
+
color: var(--current-selection-color);
|
|
36
52
|
}
|
|
53
|
+
|
|
54
|
+
&.has-current-child:not(:focus) {
|
|
55
|
+
box-shadow: inset 3px 0px 0px 0px var(--selection-parent-accent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&:hover,
|
|
59
|
+
&:focus {
|
|
60
|
+
background-color: var(--hover-background-color);
|
|
61
|
+
color: var(--hover-link-color);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
span:last-of-type {
|
|
65
|
+
color: var(--link-color);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ul ul a {
|
|
70
|
+
padding-inline-start: calc(var(--current-padding-inline) + var(--current-padding-block));
|
|
37
71
|
}
|
|
38
72
|
}
|
|
@@ -6,29 +6,64 @@ export interface SideNavItemProps {
|
|
|
6
6
|
current?: boolean
|
|
7
7
|
url: string
|
|
8
8
|
label: string
|
|
9
|
+
count?: number
|
|
10
|
+
childrenItems?: SideNavItemProps[]
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export interface SideNavProps {
|
|
12
14
|
navItems?: SideNavItemProps[]
|
|
15
|
+
className?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ItemLabel = ({ item }: { item: SideNavItemProps }) => {
|
|
19
|
+
if (typeof item.count !== "undefined") {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<span>{item.label}</span>
|
|
23
|
+
<span>{item.count}</span>
|
|
24
|
+
</>
|
|
25
|
+
)
|
|
26
|
+
} else {
|
|
27
|
+
return <>{item.label}</>
|
|
28
|
+
}
|
|
13
29
|
}
|
|
14
30
|
|
|
15
31
|
const SideNav = (props: SideNavProps) => {
|
|
16
32
|
const { LinkComponent } = React.useContext(NavigationContext)
|
|
17
33
|
|
|
34
|
+
const classNames = ["side-nav"]
|
|
35
|
+
if (props.className) classNames.push(props.className)
|
|
36
|
+
|
|
18
37
|
return (
|
|
19
|
-
<nav className="
|
|
38
|
+
<nav className={classNames.join(" ")} aria-label="Secondary navigation">
|
|
20
39
|
<ul>
|
|
21
40
|
{props.navItems?.map((navItem: SideNavItemProps, index: number) => {
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<li className="is-current" key={index} aria-current="page">
|
|
25
|
-
{navItem.label}
|
|
26
|
-
</li>
|
|
27
|
-
)
|
|
28
|
-
}
|
|
41
|
+
const hasCurrentChild = navItem.childrenItems?.some((item) => item.current)
|
|
29
42
|
return (
|
|
30
43
|
<li key={index}>
|
|
31
|
-
<LinkComponent
|
|
44
|
+
<LinkComponent
|
|
45
|
+
href={navItem.url}
|
|
46
|
+
className={hasCurrentChild ? "has-current-child" : ""}
|
|
47
|
+
aria-current={navItem.current ? "page" : undefined}
|
|
48
|
+
>
|
|
49
|
+
<ItemLabel item={navItem} />
|
|
50
|
+
</LinkComponent>
|
|
51
|
+
{navItem.childrenItems && (
|
|
52
|
+
<ul>
|
|
53
|
+
{navItem.childrenItems.map((childItem, childIndex) => {
|
|
54
|
+
return (
|
|
55
|
+
<li key={childIndex}>
|
|
56
|
+
<LinkComponent
|
|
57
|
+
href={childItem.url}
|
|
58
|
+
aria-current={childItem.current ? "page" : undefined}
|
|
59
|
+
>
|
|
60
|
+
<ItemLabel item={childItem} />
|
|
61
|
+
</LinkComponent>
|
|
62
|
+
</li>
|
|
63
|
+
)
|
|
64
|
+
})}
|
|
65
|
+
</ul>
|
|
66
|
+
)}
|
|
32
67
|
</li>
|
|
33
68
|
)
|
|
34
69
|
})}
|
|
@@ -7,6 +7,10 @@ type SiteAlertProps = {
|
|
|
7
7
|
dismissable?: boolean
|
|
8
8
|
type?: AlertTypes
|
|
9
9
|
className?: string
|
|
10
|
+
alertMessage?: {
|
|
11
|
+
type: AlertTypes
|
|
12
|
+
message: string
|
|
13
|
+
}
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export const setSiteAlertMessage = (message: string, type: AlertTypes) => {
|
|
@@ -25,9 +29,17 @@ export const SiteAlert = ({
|
|
|
25
29
|
dismissable = true,
|
|
26
30
|
type = "alert",
|
|
27
31
|
className,
|
|
32
|
+
alertMessage,
|
|
28
33
|
}: SiteAlertProps) => {
|
|
29
34
|
const [open, setOpen] = useState(false)
|
|
30
35
|
const [message, setMessage] = useState("")
|
|
36
|
+
/**
|
|
37
|
+
* We use 2 useEffects here 1 for if we are consuming what is stored in sessionStorage
|
|
38
|
+
* and another for if the message is passed in as a prop
|
|
39
|
+
|
|
40
|
+
* this gives the ability for the SiteAlert to "survive" a re-render, or if no re-render will occur
|
|
41
|
+
* for the SiteAlert to simply render itself
|
|
42
|
+
**/
|
|
31
43
|
|
|
32
44
|
useEffect(() => {
|
|
33
45
|
let timeoutRef: number
|
|
@@ -46,11 +58,23 @@ export const SiteAlert = ({
|
|
|
46
58
|
return () => clearTimeout(timeoutRef)
|
|
47
59
|
}, [timeout, type])
|
|
48
60
|
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
let timeoutRef: number
|
|
63
|
+
if (alertMessage?.message) {
|
|
64
|
+
setMessage(alertMessage?.message)
|
|
65
|
+
setOpen(true)
|
|
66
|
+
if (timeout) {
|
|
67
|
+
timeoutRef = (setTimeout(() => setOpen(false), timeout) as unknown) as number
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return () => clearTimeout(timeoutRef)
|
|
71
|
+
}, [alertMessage, timeout])
|
|
72
|
+
|
|
49
73
|
return open ? (
|
|
50
74
|
<AlertBox
|
|
51
75
|
onClose={dismissable ? () => setOpen(false) : undefined}
|
|
52
76
|
className={className}
|
|
53
|
-
type={type}
|
|
77
|
+
type={alertMessage?.type ?? type}
|
|
54
78
|
>
|
|
55
79
|
{message}
|
|
56
80
|
</AlertBox>
|