Skip to content

Commit

Permalink
Add banner
Browse files Browse the repository at this point in the history
  • Loading branch information
coskucinkilic committed Oct 18, 2024
1 parent 836cde0 commit 67c468c
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/docs/constants/docs.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ export const COMPONENT_ROUTES: ComponentRoute[] = [

description: "A main banner for landing pages.",
},
{
path: "/components/banner",
title: "Banner",

description: "Inside page banner",
},

{
path: "/components/popover",
Expand Down
1 change: 1 addition & 0 deletions src/lib/components.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { default as Back } from "./components/Back.svelte";
export { default as Backdrop } from "./components/Backdrop.svelte";
export { default as Banner } from "./components/Banner.svelte";
export { default as BottomSheet } from "./components/BottomSheet.svelte";
export { default as BusyScreen } from "./components/BusyScreen.svelte";
export { default as Card } from "./components/Card.svelte";
Expand Down
84 changes: 84 additions & 0 deletions src/lib/components/Banner.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<script lang="ts">
export let testId: string | undefined = undefined;
import { DEFAULT_ICON_SIZE } from "$lib/constants/constants";
import IconClose from "$lib/icons/IconClose.svelte";
import { createEventDispatcher } from "svelte";
export let visible: boolean = true;
const dispatch = createEventDispatcher();
function setVisibilityFalse() {
visible = false;
dispatch("nnsClose");
}
</script>

{#if visible}
<div
class="banner"
data-tid={testId}
style="--default-icon-size: {DEFAULT_ICON_SIZE}px;"
>
<div class="icon-background">
<div class="icon-wrapper">
<slot name="icon" />
</div>
</div>
<div class="text-content">
<slot name="title" />
<slot name="description" />
</div>
<div class="action-wrapper">
<slot name="action" />
</div>
<button class="close-button icon-only" on:click={setVisibilityFalse}>
<IconClose />
</button>
</div>
{/if}

<style lang="scss">
@use "../styles/mixins/fonts";
.banner {
display: flex;
align-items: center;
background: var(--banner-background, var(--input-background));
border-radius: var(--banner-radius, var(--border-radius));
padding: var(--banner-top-bottom-padding, var(--padding))
var(--banner-left-right-padding, var(--padding-1_5x));
column-gap: var(--banner-column-gap, var(--padding-1_5x));
}
.icon-background {
padding: var(--icon-background-padding, 6px);
background-color: var(
--icon-background-color,
var(--dropdown-border-color)
);
border-radius: var(--icon-border-radius, 50%);
}
.icon-wrapper {
color: var(--icon-color, var(--elements-icons));
}
.action-wrapper {
flex-shrink: 0;
--button-min-height: 40px;
}
.text-content {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.close-button {
padding: var(--close-button-padding, 10px);
border-radius: var(--close-button-border-radius, var(--border-radius));
background: var(--close-button-background, var(--card-background));
color: var(--elements-icons);
}
</style>
2 changes: 2 additions & 0 deletions src/lib/styles/global/fonts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ textarea {
--font-size-h4: 1.142857142857143rem; // 16px / 14px
--font-size-h5: 1rem;
--font-size-standard: 16px;
--font-size-secondary: 0.875rem; // 14px
--font-size-small: 0.785714285714286rem; // 11px / 14px

--line-height-h1: 1.333333333333333; // 32
Expand All @@ -63,6 +64,7 @@ textarea {
--line-height-h4: 1.25; // 20
--line-height-h5: var(--line-height-standard);
--line-height-standard: 1.142857142857143; // 16 / 14 because 14px was the original base font size defined by the designer to calculate that family of font sizes
--line-height-secondary: 1.285714285714286; // 18
--line-height-small: 1.181818181818182; // 13

--font-weight-bold: 450;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/styles/mixins/_fonts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
@include bold($bold);
}

@mixin secondary($bold: false) {
font-size: var(--font-size-secondary);
line-height: var(--line-height-secondary);
@include bold($bold);
}

@mixin small($bold: false) {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
Expand Down
132 changes: 132 additions & 0 deletions src/routes/(split)/components/banner/+page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<script lang="ts">
import Banner from "$lib/components/Banner.svelte";
import IconInfo from "$lib/icons/IconInfo.svelte";
</script>

# Banner

A versatile component to render informative banners with customizable content, actions, and visibility control.

| Property | Description | Type | Default |
| --------- | --------------------------------------------------------------- | ----------------------- | ----------- |
| `testId` | Add a `data-tid` attribute to the DOM, useful for test purpose. | `string` or `undefined` | `undefined` |
| `visible` | Controls the visibility of the banner. | `boolean` | `true` |

## Slots

| Slot name | Description |
| ------------- | --------------------------------------------------------------------------- |
| `icon` | Icon appearing at the start of the banner. |
| `title` | Title of the banner. |
| `description` | Description text below the title. It should explain the banner's purpose. |
| `action` | Located on the right side of the banner. Useful for call-to-action buttons. |

## Events

| Event | Description |
| ---------- | -------------------------------------------- |
| `nnsClose` | Dispatched when the close button is clicked. |

## Showcase

The component has its own background color and visibility control, which can be customized using props and CSS variables.

### Example

<Banner>
<IconInfo slot="icon" />
<span slot="title" class='title'>Your ICP Wallet</span>
<span slot="description" class="description">
Create and link accounts, transfer ICP, participate in governance, and earn
rewards.
</span>
<button class="secondary" slot="action">Make Neurons Public</button>
</Banner>

<br />

<style lang="scss">
@use "../../../../lib/styles/mixins/fonts";

.title {
@include fonts.secondary(true);
}
.description {
@include fonts.secondary(false);
}
</style>

#### Code

```text
<Banner>
<IconInfo slot="icon" />
<span slot="title">Your ICP Wallet</span>
<span slot="description" class="description">
Create and link accounts, transfer ICP, participate in governance, and earn
rewards.
</span>
<button class="primary" slot="action">Connect Wallet</button>
</Banner>
<style lang="scss">
@use "../../../../lib/styles/mixins/fonts";
.title {
@include fonts.secondary(true);
}
.description {
@include fonts.secondary(false);
}
</style>
```

## Visibility Control

The Banner component includes built-in visibility control:

- The `visible` prop controls the visibility of the banner.
- When the close button is clicked, the `visible` prop is set to `false` and an `nnsClose` event is dispatched.

## Customization

You can customize the appearance of the Banner component using CSS variables:

| CSS Variable | Description | Default Value |
| ------------------------------ | ------------------------------------ | ------------------------------ |
| `--banner-background` | Background color of the banner | `var(--input-background)` |
| `--banner-radius` | Border radius of the banner | `var(--border-radius)` |
| `--banner-top-bottom-padding` | Vertical padding inside the banner | `var(--padding)` |
| `--banner-left-right-padding` | Horizontal padding inside the banner | `var(--padding-1_5x)` |
| `--banner-column-gap` | Gap between banner elements | `var(--padding-1_5x)` |
| `--icon-background-padding` | Padding around the icon | `6px` |
| `--icon-background-color` | Background color of the icon wrapper | `var(--dropdown-border-color)` |
| `--icon-border-radius` | Border radius of the icon wrapper | `50%` |
| `--icon-color` | Color of the icon | `var(--elements-icons)` |
| `--close-button-padding` | Padding of the close button | `10px` |
| `--close-button-border-radius` | Border radius of the close button | `var(--border-radius)` |
| `--close-button-background` | Background color of the close button | `var(--card-background)` |

Example of customization:

```svelte
<Banner>
<!-- Banner content -->
</Banner>
<style lang="scss">
:global(.banner) {
--banner-background: #f0f8ff;
--banner-radius: 10px;
--banner-top-bottom-padding: 12px;
--banner-left-right-padding: 18px;
--banner-column-gap: 18px;
--icon-background-color: #e6f3ff;
--icon-color: #0056b3;
--close-button-background: #ffffff;
}
</style>
```

This customization will apply a light blue theme to the banner and adjust the padding, border radius, and colors.
49 changes: 49 additions & 0 deletions src/tests/lib/components/Banner.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Banner from "$lib/components/Banner.svelte";
import { fireEvent, render } from "@testing-library/svelte";
import BannerTest from "./BannerTest.svelte";

describe("Banner", () => {
it("should render the banner when visible is true", () => {
const { container } = render(Banner);
expect(container.querySelector(".banner")).not.toBeNull();
});

it("should not render the banner when visible is false", () => {
const { container } = render(Banner, { props: { visible: false } });
expect(container.querySelector(".banner")).toBeNull();
});

it("should render slotted content", () => {
const { getByText } = render(BannerTest);
expect(getByText("Test Icon")).toBeInTheDocument();
expect(getByText("Test Title")).toBeInTheDocument();
expect(getByText("Test Description")).toBeInTheDocument();
expect(getByText("Test Action")).toBeInTheDocument();
});

it("should close the banner when close button is clicked", async () => {
const { container, component } = render(Banner);

const closeButton = container.querySelector(
".close-button",
) as HTMLButtonElement;
expect(closeButton).not.toBeNull();

const onClose = vi.fn();
component.$on("nnsClose", onClose);

await fireEvent.click(closeButton);

expect(onClose).toHaveBeenCalled();
expect(container.querySelector(".banner")).toBeNull();
});

it("should apply custom test ID", () => {
const testId = "custom-banner-id";
const { container } = render(Banner, { props: { testId } });

const banner = container.querySelector(".banner");
expect(banner).not.toBeNull();
expect(banner?.getAttribute("data-tid")).toBe(testId);
});
});
10 changes: 10 additions & 0 deletions src/tests/lib/components/BannerTest.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import Banner from "$lib/components/Banner.svelte";
</script>

<Banner>
<span slot="icon">Test Icon</span>
<span slot="title">Test Title</span>
<span slot="description">Test Description</span>
<button slot="action">Test Action</button>
</Banner>

0 comments on commit 67c468c

Please sign in to comment.