@cerebruminc/cerebellum 19.2.0 → 20.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.
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/src/components/UserInformationCard/UserInformationCard.module.css +212 -0
- package/src/components/UserInformationCard/UserInformationCard.stories.tsx +31 -9
- package/src/components/UserInformationCard/UserInformationCard.test.tsx +33 -4
- package/src/components/UserInformationCard/UserInformationCard.tsx +171 -120
- package/src/components/UserInformationCard/types.ts +4 -57
- package/src/index.ts +1 -1
- package/src/jest-setup.ts +7 -0
- package/src/mantine/components/Pill.module.css +32 -0
- package/src/mantine/components/ScrollArea.module.css +32 -0
- package/src/mantine/mantineAugmentations.ts +5 -0
- package/src/mantine/mantineTheme.ts +72 -0
- package/src/mantine/stories/Pill.stories.tsx +169 -0
- package/src/mantine/types.ts +3 -0
- package/src/components/UserInformationCard/UserInformationCardStyles.tsx +0 -186
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# react-component-lib-boilerplate
|
|
2
2
|
|
|
3
|
+
## [20.0.0](https://github.com/cerebruminc/cerebellum/compare/v19.2.0...v20.0.0) (2026-08-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* **userInformationCard:** The themeOverride prop has been removed. Use the new avatarBackgroundColor prop directly instead of themeOverride.avatarBackgroundColor. The other themeOverride sub-properties (gradientSectionHeight, avatarSize, backgroundGradient, childrenDividerColor) were unused externally and are removed.
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **pill:** style Mantine Pill to match ClearableTag in mantineTheme.ts ([6460c74](https://github.com/cerebruminc/cerebellum/commit/6460c7467a45f19a3e3bb7e6d12d7315f1e3706c))
|
|
13
|
+
* **scrollArea:** add ScrollArea theming to mantineTheme ([2f857be](https://github.com/cerebruminc/cerebellum/commit/2f857bef91765552bedea28b51ab3d1331a6af75))
|
|
14
|
+
* **userInformationCard:** refactor to Mantine components and CSS modules ([e31191b](https://github.com/cerebruminc/cerebellum/commit/e31191b49da865225509506dfa8a20e9a1eb217d))
|
|
15
|
+
|
|
3
16
|
## [19.2.0](https://github.com/cerebruminc/cerebellum/compare/v19.1.1...v19.2.0) (2026-07-30)
|
|
4
17
|
|
|
5
18
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/* UserInformationCard CSS module */
|
|
2
|
+
|
|
3
|
+
/* ---- CardBox ---- */
|
|
4
|
+
.cardBox {
|
|
5
|
+
background-color: var(--mantine-color-white);
|
|
6
|
+
border-radius: 10px;
|
|
7
|
+
box-shadow: var(--uic-box-shadow, 0 14px 68.6px 1.4px rgba(0, 0, 0, 0.13));
|
|
8
|
+
display: block;
|
|
9
|
+
flex-flow: column nowrap;
|
|
10
|
+
max-height: var(--uic-max-height, none);
|
|
11
|
+
max-width: 100%;
|
|
12
|
+
min-width: 310px;
|
|
13
|
+
width: var(--uic-width, 100%);
|
|
14
|
+
}
|
|
15
|
+
.cardBox[data-horizontal] {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-flow: row nowrap;
|
|
18
|
+
}
|
|
19
|
+
@media print {
|
|
20
|
+
.cardBox {
|
|
21
|
+
flex-flow: row nowrap;
|
|
22
|
+
print-color-adjust: exact;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ---- AvatarWrapper ---- */
|
|
27
|
+
.avatarWrapper {
|
|
28
|
+
background: linear-gradient(var(--uic-gradient));
|
|
29
|
+
border-radius: 10px 10px 0 0;
|
|
30
|
+
display: block;
|
|
31
|
+
min-height: var(--uic-gradient-height);
|
|
32
|
+
padding: 19px 22px;
|
|
33
|
+
position: relative;
|
|
34
|
+
z-index: 2;
|
|
35
|
+
}
|
|
36
|
+
.avatarWrapper[data-horizontal] {
|
|
37
|
+
align-items: center;
|
|
38
|
+
border-radius: 10px 0 0 10px;
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-flow: column nowrap;
|
|
41
|
+
gap: 15px;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
min-height: 186px;
|
|
44
|
+
min-width: 186px;
|
|
45
|
+
}
|
|
46
|
+
@media print {
|
|
47
|
+
.avatarWrapper {
|
|
48
|
+
align-items: center;
|
|
49
|
+
border-radius: 10px 0 0 10px;
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-flow: column nowrap;
|
|
52
|
+
gap: 15px;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
min-height: 186px;
|
|
55
|
+
min-width: 186px;
|
|
56
|
+
print-color-adjust: exact;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ---- Avatar container (wraps Mantine Avatar + optional badge) ---- */
|
|
61
|
+
/* Vertical mode: center avatar below the gradient */
|
|
62
|
+
.avatarContainer {
|
|
63
|
+
bottom: 0;
|
|
64
|
+
display: inline-block;
|
|
65
|
+
left: 50%;
|
|
66
|
+
position: absolute;
|
|
67
|
+
transform: translate(-50%, 35px);
|
|
68
|
+
}
|
|
69
|
+
.avatarWrapper[data-horizontal] .avatarContainer {
|
|
70
|
+
position: static;
|
|
71
|
+
bottom: auto;
|
|
72
|
+
left: auto;
|
|
73
|
+
transform: none;
|
|
74
|
+
}
|
|
75
|
+
@media print {
|
|
76
|
+
.avatarContainer {
|
|
77
|
+
position: relative;
|
|
78
|
+
bottom: inherit;
|
|
79
|
+
left: inherit;
|
|
80
|
+
transform: none;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.badge {
|
|
85
|
+
background-repeat: no-repeat;
|
|
86
|
+
background-size: contain;
|
|
87
|
+
border-radius: 50%;
|
|
88
|
+
bottom: 0;
|
|
89
|
+
height: var(--uic-badge-size);
|
|
90
|
+
left: 50%;
|
|
91
|
+
position: absolute;
|
|
92
|
+
transform: translate(-46%, 40%);
|
|
93
|
+
width: var(--uic-badge-size);
|
|
94
|
+
z-index: 10;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ---- HorizontalMeasure ---- */
|
|
98
|
+
.horizontalMeasure {
|
|
99
|
+
width: 100%;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ---- DataWrapper ---- */
|
|
103
|
+
.dataWrapper {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-flow: column nowrap;
|
|
106
|
+
gap: 28px;
|
|
107
|
+
padding: 60px 42px 52px 42px;
|
|
108
|
+
width: 100%;
|
|
109
|
+
}
|
|
110
|
+
.dataWrapper[data-has-children] {
|
|
111
|
+
padding-bottom: 34px;
|
|
112
|
+
}
|
|
113
|
+
.dataWrapper[data-horizontal] {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: 1fr 1fr 1fr minmax(100px, 1fr) minmax(100px, 1fr);
|
|
116
|
+
grid-template-areas:
|
|
117
|
+
"name name name dob ssn"
|
|
118
|
+
"address address address address address";
|
|
119
|
+
padding: 33px 65px 35px 33px;
|
|
120
|
+
}
|
|
121
|
+
.dataWrapper[data-horizontal][data-break-ssn] {
|
|
122
|
+
grid-template-columns: 1fr 1fr 1fr 1fr minmax(100px, 1fr);
|
|
123
|
+
grid-template-areas:
|
|
124
|
+
"name name name name dob"
|
|
125
|
+
"address address address address ssn";
|
|
126
|
+
}
|
|
127
|
+
.dataWrapper[data-horizontal][data-break-layout] {
|
|
128
|
+
grid-template-columns: 1fr;
|
|
129
|
+
grid-template-areas: "name" "address" "dob" "ssn";
|
|
130
|
+
}
|
|
131
|
+
@media print {
|
|
132
|
+
.dataWrapper {
|
|
133
|
+
display: grid;
|
|
134
|
+
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
135
|
+
grid-template-areas:
|
|
136
|
+
"name name name name dob"
|
|
137
|
+
"address address address address ssn";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* ---- Item ---- */
|
|
142
|
+
.item {
|
|
143
|
+
display: flex;
|
|
144
|
+
flex-flow: column nowrap;
|
|
145
|
+
gap: 5px;
|
|
146
|
+
grid-area: var(--uic-grid-area);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* ---- Label ---- */
|
|
150
|
+
.label {
|
|
151
|
+
color: var(--mantine-color-gray-8);
|
|
152
|
+
font-size: 15px;
|
|
153
|
+
font-weight: 600;
|
|
154
|
+
width: 100%;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ---- OrderTag ---- */
|
|
158
|
+
.orderTag {
|
|
159
|
+
background-color: #8da7ff; /* BLUE_35 — no exact Mantine shade */
|
|
160
|
+
border-radius: 6px;
|
|
161
|
+
color: var(--mantine-color-white);
|
|
162
|
+
display: inline-block;
|
|
163
|
+
font-size: 16px;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
max-width: calc(var(--uic-order-tag-max-width, 186px) - 44px);
|
|
166
|
+
padding: 6px 17px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ---- ChildWrapper ---- */
|
|
170
|
+
.childWrapper {
|
|
171
|
+
padding: 0 42px 52px 42px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ---- Divider ---- */
|
|
175
|
+
.divider {
|
|
176
|
+
background-color: var(--mantine-color-gray-4);
|
|
177
|
+
border: none;
|
|
178
|
+
height: 1px;
|
|
179
|
+
margin: 0 -9px 8px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ---- ClearableTagWrapper ---- */
|
|
183
|
+
.clearableTagWrapper {
|
|
184
|
+
margin-top: -15px;
|
|
185
|
+
margin-bottom: -10px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Remove the shaded background behind the Pill close button */
|
|
189
|
+
.pillRemove::before {
|
|
190
|
+
display: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.pillIcon {
|
|
194
|
+
align-items: center;
|
|
195
|
+
display: inline-flex;
|
|
196
|
+
flex-shrink: 0;
|
|
197
|
+
height: 14px;
|
|
198
|
+
margin-right: 6px;
|
|
199
|
+
opacity: 0.8;
|
|
200
|
+
width: 14px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ---- Text styling ---- */
|
|
204
|
+
.titleText {
|
|
205
|
+
overflow-wrap: anywhere;
|
|
206
|
+
}
|
|
207
|
+
.titleText[data-center] {
|
|
208
|
+
text-align: center;
|
|
209
|
+
}
|
|
210
|
+
.bodyText {
|
|
211
|
+
overflow-wrap: anywhere;
|
|
212
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { Text } from "@mantine/core";
|
|
1
2
|
import { Meta, StoryObj } from "@storybook/react";
|
|
2
3
|
import { Fragment } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { IconTile } from "../IconTile/IconTile";
|
|
5
|
-
import { Employee, User, UserRecords } from "../Icons";
|
|
4
|
+
import { User, UserRecords } from "../Icons";
|
|
6
5
|
import { UserInformationCard } from "./UserInformationCard";
|
|
7
6
|
|
|
8
7
|
/** `UserInformationCard` is a display component that renders a card, which contains a gradient with an Avatar and information about a User.
|
|
@@ -66,6 +65,8 @@ export const PictureAvatar: Story = {
|
|
|
66
65
|
},
|
|
67
66
|
};
|
|
68
67
|
|
|
68
|
+
/** Demonstrates the loading state, which renders Mantine Skeleton components
|
|
69
|
+
* in place of the text fields while data is being fetched. */
|
|
69
70
|
export const LoadingState: Story = {
|
|
70
71
|
args: {
|
|
71
72
|
avatarImageUrl: "https://picsum.photos/200",
|
|
@@ -102,10 +103,15 @@ export const Children: Story = {
|
|
|
102
103
|
|
|
103
104
|
children: (
|
|
104
105
|
<Fragment>
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<
|
|
106
|
+
<Text variant="title-md" component="h2">
|
|
107
|
+
Put whatever you want in here
|
|
108
|
+
</Text>
|
|
109
|
+
<Text variant="body-md" c="gray.7" component="p">
|
|
110
|
+
Custom content below the card data
|
|
111
|
+
</Text>
|
|
112
|
+
<Text variant="title-md" component="h2">
|
|
113
|
+
Make it look better than this
|
|
114
|
+
</Text>
|
|
109
115
|
</Fragment>
|
|
110
116
|
),
|
|
111
117
|
},
|
|
@@ -129,7 +135,8 @@ export const AlternateMode: Story = {
|
|
|
129
135
|
},
|
|
130
136
|
};
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
/** Demonstrates the `avatarBackgroundColor` prop, which overrides the default gradient on the Avatar placeholder. */
|
|
139
|
+
export const AvatarBackground: Story = {
|
|
133
140
|
args: {
|
|
134
141
|
orderType: "Nope",
|
|
135
142
|
firstName: "Don't do it",
|
|
@@ -139,6 +146,21 @@ export const ThemeOverride: Story = {
|
|
|
139
146
|
dob: "07-08-xxxx",
|
|
140
147
|
ssn: "xxx-xx-1234",
|
|
141
148
|
width: 310,
|
|
142
|
-
|
|
149
|
+
avatarBackgroundColor: "red",
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** Demonstrates a clearable message (Pill) with `isClearable` set to false, rendering the Pill without a remove button. */
|
|
154
|
+
export const NonClearableMessage: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
firstName: "Dave",
|
|
157
|
+
lastName: "Bascom",
|
|
158
|
+
address1: "P.O. Box 7340",
|
|
159
|
+
dob: "07-08-xxxx",
|
|
160
|
+
ssn: "xxx-xx-1234",
|
|
161
|
+
width: 310,
|
|
162
|
+
clearableMessage: "Applicant-Added Data",
|
|
163
|
+
ClearableMessageIcon: UserRecords,
|
|
164
|
+
isClearable: false,
|
|
143
165
|
},
|
|
144
166
|
};
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { MantineProvider } from "@mantine/core";
|
|
1
2
|
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { ComponentType, FC } from "react";
|
|
4
5
|
import { UserInformationCard } from "./UserInformationCard";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
function withProviders<P extends {}>(Component: ComponentType<P>): FC<P> {
|
|
8
|
+
return (props: P) => (
|
|
9
|
+
<MantineProvider>
|
|
10
|
+
<Component {...props} />
|
|
11
|
+
</MantineProvider>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ThemedInformationCard = withProviders(UserInformationCard);
|
|
7
16
|
|
|
8
17
|
describe("UserInformationCard", () => {
|
|
9
18
|
test("renders UserInformationCard", () => {
|
|
@@ -92,9 +101,29 @@ describe("UserInformationCard", () => {
|
|
|
92
101
|
const childText = "This is some kind of text, passed to the child component";
|
|
93
102
|
render(
|
|
94
103
|
<ThemedInformationCard firstName="Foo" lastName="Bar">
|
|
95
|
-
<
|
|
104
|
+
<p>{childText}</p>
|
|
96
105
|
</ThemedInformationCard>
|
|
97
106
|
);
|
|
98
107
|
expect(screen.getByText(childText)).toBeInTheDocument();
|
|
99
108
|
});
|
|
109
|
+
|
|
110
|
+
test("renders clearableMessage as a Pill", () => {
|
|
111
|
+
render(<ThemedInformationCard firstName="Foo" lastName="Bar" clearableMessage="Applicant-Added Data" />);
|
|
112
|
+
expect(screen.getByText("Applicant-Added Data")).toBeInTheDocument();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("clearableMessage remove button hides the Pill when clicked", async () => {
|
|
116
|
+
const user = userEvent.setup();
|
|
117
|
+
render(<ThemedInformationCard firstName="Foo" lastName="Bar" clearableMessage="Applicant-Added Data" isClearable />);
|
|
118
|
+
expect(screen.getByText("Applicant-Added Data")).toBeInTheDocument();
|
|
119
|
+
const removeButton = screen.getByRole("button", { hidden: true });
|
|
120
|
+
await user.click(removeButton);
|
|
121
|
+
expect(screen.queryByText("Applicant-Added Data")).not.toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("clearableMessage without isClearable does not render a remove button", () => {
|
|
125
|
+
render(<ThemedInformationCard firstName="Foo" lastName="Bar" clearableMessage="Applicant-Added Data" isClearable={false} />);
|
|
126
|
+
expect(screen.getByText("Applicant-Added Data")).toBeInTheDocument();
|
|
127
|
+
expect(screen.queryByRole("button", { hidden: true })).not.toBeInTheDocument();
|
|
128
|
+
});
|
|
100
129
|
});
|