Skip to content

Commit 006b651

Browse files
kaylendogHalf-Shot
authored andcommitted
Implement a shared Banner component. (element-hq#31266)
* feat: Create composer `Banner` shared component. * fix: Yarn resolution issues corrupting package store. * chore: Revert "fix: Yarn resolution issues corrupting package store." This reverts commit 2c13354. * fix: Revert lockfile changes. * chore: Resolve linting errors. * chore: Update playwright screenshots.
1 parent 2d33d91 commit 006b651

16 files changed

Lines changed: 610 additions & 40 deletions

packages/shared-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"test:storybook:update": "playwright-screenshots --entrypoint yarn --with-node-modules && playwright-screenshots --entrypoint /work/node_modules/.bin/test-storybook --with-node-modules --url http://host.docker.internal:6007/ --updateSnapshot"
4747
},
4848
"dependencies": {
49+
"@vector-im/compound-design-tokens": "^6.3.0",
4950
"classnames": "^2.5.1",
5051
"counterpart": "^0.18.6",
5152
"lodash": "^4.17.21",
@@ -88,7 +89,6 @@
8889
},
8990
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
9091
"peerDependencies": {
91-
"@vector-im/compound-design-tokens": "^6.0.0",
9292
"@vector-im/compound-web": "^8.2.5"
9393
}
9494
}
12.6 KB
Loading
10.3 KB
Loading
13.1 KB
Loading
13.7 KB
Loading
16.9 KB
Loading
9.51 KB
Loading
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2025 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
:root {
9+
--cpd-color-gradient-critical-linear: linear-gradient(
10+
180deg,
11+
var(--cpd-color-alpha-red-500) 0%,
12+
var(--cpd-color-alpha-red-400) 20%,
13+
var(--cpd-color-alpha-red-300) 40%,
14+
var(--cpd-color-alpha-red-200) 60%,
15+
var(--cpd-color-alpha-red-100) 80%,
16+
var(--cpd-color-transparent) 100%
17+
);
18+
}
19+
20+
.banner {
21+
container-type: inline-size;
22+
container-name: banner;
23+
display: flex;
24+
align-items: center;
25+
justify-content: start;
26+
gap: var(--cpd-space-3x);
27+
padding: var(--cpd-space-4x);
28+
29+
border-top: 1px solid var(--cpd-color-gray-400);
30+
31+
white-space: nowrap;
32+
}
33+
34+
.banner[data-type="success"] {
35+
background: var(--cpd-color-gradient-subtle-linear);
36+
border-color: var(--cpd-color-green-900);
37+
}
38+
39+
.banner[data-type="critical"] {
40+
background: var(--cpd-color-gradient-critical-linear);
41+
border-color: var(--cpd-color-border-critical-primary);
42+
}
43+
44+
.banner[data-type="info"] {
45+
background: var(--cpd-color-gradient-info-linear);
46+
border-color: var(--cpd-color-blue-900);
47+
}
48+
49+
.banner[data-type="info"] :is(svg) {
50+
color: var(--cpd-color-blue-900);
51+
}
52+
53+
.banner[data-type="success"] :is(.content, svg) {
54+
color: var(--cpd-color-green-900);
55+
}
56+
57+
.banner[data-type="critical"] :is(.content, svg) {
58+
color: var(--cpd-color-red-900);
59+
}
60+
61+
.banner p {
62+
margin: 0;
63+
}
64+
65+
.icon {
66+
/* lock icon dimensions */
67+
min-width: 32px;
68+
min-height: 32px;
69+
max-width: 32px;
70+
max-height: 32px;
71+
72+
margin: 4px;
73+
74+
/* centre svg icons, as they are not full width */
75+
flex: 0;
76+
display: flex;
77+
align-items: center;
78+
justify-content: center;
79+
}
80+
81+
.icon img {
82+
border-radius: 50%;
83+
}
84+
85+
.actions {
86+
margin-left: auto;
87+
88+
flex: 0;
89+
display: flex;
90+
flex-direction: row;
91+
gap: var(--cpd-space-1x);
92+
align-self: center;
93+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React from "react";
9+
import { fn } from "storybook/test";
10+
import { type Meta, type StoryObj } from "@storybook/react-vite";
11+
import { Button } from "@vector-im/compound-web";
12+
13+
import { Banner } from "./Banner";
14+
import { _t } from "../../utils/i18n";
15+
16+
const meta = {
17+
title: "room/Banner",
18+
component: Banner,
19+
tags: ["autodocs"],
20+
args: {
21+
children: <p>Hello! This is a status banner.</p>,
22+
onClose: fn(),
23+
},
24+
} satisfies Meta<typeof Banner>;
25+
26+
export default meta;
27+
type Story = StoryObj<typeof meta>;
28+
29+
export const Default: Story = {};
30+
export const Info: Story = {
31+
args: {
32+
type: "info",
33+
},
34+
};
35+
export const Success: Story = {
36+
args: {
37+
type: "success",
38+
},
39+
};
40+
export const Critical: Story = {
41+
args: {
42+
type: "critical",
43+
},
44+
};
45+
export const WithAction: Story = {
46+
args: {
47+
children: (
48+
<p>
49+
{_t(
50+
"encryption|pinned_identity_changed",
51+
{ displayName: "Alice", userId: "@alice:example.org" },
52+
{
53+
a: (sub) => <a href="https://example.org">{sub}</a>,
54+
b: (sub) => <b>{sub}</b>,
55+
},
56+
)}
57+
</p>
58+
),
59+
actions: <Button kind="primary">{_t("encryption|withdraw_verification_action")}</Button>,
60+
},
61+
};
62+
63+
export const WithAvatarImage: Story = {
64+
args: {
65+
avatar: <img alt="Example" src="https://picsum.photos/32/32" />,
66+
},
67+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2025 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React from "react";
9+
import { render } from "jest-matrix-react";
10+
import { composeStories } from "@storybook/react-vite";
11+
12+
import * as stories from "./Banner.stories.tsx";
13+
14+
const { Default, Info, Success, WithAction, WithAvatarImage, Critical } = composeStories(stories);
15+
16+
describe("AvatarWithDetails", () => {
17+
it("renders a default banner", () => {
18+
const { container } = render(<Default />);
19+
expect(container).toMatchSnapshot();
20+
});
21+
it("renders a info banner", () => {
22+
const { container } = render(<Info />);
23+
expect(container).toMatchSnapshot();
24+
});
25+
it("renders a success banner", () => {
26+
const { container } = render(<Success />);
27+
expect(container).toMatchSnapshot();
28+
});
29+
it("renders a critical banner", () => {
30+
const { container } = render(<Critical />);
31+
expect(container).toMatchSnapshot();
32+
});
33+
it("renders a banner with an action", () => {
34+
const { container } = render(<WithAction />);
35+
expect(container).toMatchSnapshot();
36+
});
37+
it("renders a banner with an avatar iamge", () => {
38+
const { container } = render(<WithAvatarImage />);
39+
expect(container).toMatchSnapshot();
40+
});
41+
});

0 commit comments

Comments
 (0)