@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerebruminc/cerebellum",
3
- "version": "19.2.0",
3
+ "version": "20.0.0",
4
4
  "description": "Cerebrum's React Component Library",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -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 { HeadingH2 } from "../../const/text";
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
- <HeadingH2>Put whatever you want in here</HeadingH2>
106
- <IconTile Icon={Employee} label="Employee" onButtonClick={() => console.log("onButtonClick")} />
107
- <HeadingH2>Make it look better than this</HeadingH2>
108
- <IconTile Icon={Employee} label="Employee" onButtonClick={() => console.log("onButtonClick")} />
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
- export const ThemeOverride: Story = {
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
- themeOverride: { gradientSectionHeight: 110, avatarBackgroundColor: "red", avatarSize: 150, backgroundGradient: "blue, green" },
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 { HeadingH3 } from "../../const/text";
3
- import { withTheme } from "../../hocs/withTheme";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { ComponentType, FC } from "react";
4
5
  import { UserInformationCard } from "./UserInformationCard";
5
6
 
6
- const ThemedInformationCard = withTheme(UserInformationCard);
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
- <HeadingH3>{childText}</HeadingH3>
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
  });