Skip to content

Commit

Permalink
add ThemeToggleButton, tests and doc (#482)
Browse files Browse the repository at this point in the history
# Motivation

Add new ThemeToggleButton component 

# Changes

ThemeToggleButton component added to repo
Tests for ThemeToggleButton added to spec file
ThemeToggleButton is added to showcase
--icon-color variable added to IconDarkMode and IconLightMode
New --sidebar-icon variable has been added to themes

# Screenshots
<img width="1325" alt="Screenshot 2024-09-17 at 14 16 49"
src="https://github.com/user-attachments/assets/9f142485-467c-4561-a3ae-638662fb724d">
<img width="1327" alt="Screenshot 2024-09-17 at 14 15 09"
src="https://github.com/user-attachments/assets/a4ba4458-ca81-43ed-8d0c-b231dc63ec58">

---------

Co-authored-by: “Cosku <“cosku.cinkilic@dfinity.org”>
  • Loading branch information
coskucinkilic and “Cosku committed Sep 17, 2024
1 parent eafc935 commit 9a16f6f
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 2 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 @@ -260,6 +260,12 @@ export const COMPONENT_ROUTES: ComponentRoute[] = [
description: "An opinionated theme toggle.",
},

{
path: "/components/theme-toggle-button",
title: "Theme Toggle Button",
description: "An opinionated theme toggle button.",
},

{
path: "/components/tooltip",
title: "Tooltip",
Expand Down
1 change: 1 addition & 0 deletions src/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export { default as SplitContent } from "./components/SplitContent.svelte";
export { default as SplitPane } from "./components/SplitPane.svelte";
export { default as Tag } from "./components/Tag.svelte";
export { default as ThemeToggle } from "./components/ThemeToggle.svelte";
export { default as ThemeToggleButton } from "./components/ThemeToggleButton.svelte";
export { default as Toasts } from "./components/Toasts.svelte";
export { default as Toggle } from "./components/Toggle.svelte";
export { default as Toolbar } from "./components/Toolbar.svelte";
Expand Down
49 changes: 49 additions & 0 deletions src/lib/components/ThemeToggleButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts">
import { Theme } from "$lib/types/theme";
import { themeStore } from "$lib/stores/theme.store";
import { i18n } from "$lib/stores/i18n";
import IconLightMode from "$lib/icons/IconLightMode.svelte";
import IconDarkMode from "$lib/icons/IconDarkMode.svelte";
import { fade } from "svelte/transition";
const switchTheme = () => {
themeStore.select($themeStore === Theme.LIGHT ? Theme.DARK : Theme.LIGHT);
};
let isDarkMode: boolean;
$: isDarkMode = $themeStore === Theme.DARK;
</script>

<button
data-tid="theme-toggle-button"
class="theme-toggle-button icon-only"
on:click={switchTheme}
aria-label={$i18n.theme.switch_theme}
>
{#if isDarkMode}
<span in:fade|global>
<IconLightMode />
</span>
{:else}
<span in:fade|global>
<IconDarkMode />
</span>
{/if}
</button>

<style lang="scss">
.theme-toggle-button {
// Height and width base on icon size + padding
// To fix the issue of ThemeToggle being stretched by parents
height: calc(var(--padding-2x) + 20px);
width: calc(var(--padding-2x) + 20px);
color: var(--sidebar-icon);
padding: var(--padding);
background: var(--sidebar-button-background);
line-height: 0;
&:hover {
background: var(--sidebar-button-background-hover);
--icon-fill: var(--sidebar-icon);
}
}
</style>
2 changes: 1 addition & 1 deletion src/lib/icons/IconDarkMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
height={DEFAULT_ICON_SIZE}
viewBox="0 0 20 20"
width={DEFAULT_ICON_SIZE}
fill="none"
fill="var(--icon-fill, none)"
stroke="currentColor"
>
<path
Expand Down
2 changes: 1 addition & 1 deletion src/lib/icons/IconLightMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
height={DEFAULT_ICON_SIZE}
viewBox="0 0 20 20"
width={DEFAULT_ICON_SIZE}
fill="none"
fill="var(--icon-fill, none)"
stroke="currentColor"
>
<path
Expand Down
1 change: 1 addition & 0 deletions src/lib/styles/themes/dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
// Sidebar
--sidebar-button-background: var(--cp-dark-1000-20);
--sidebar-button-background-hover: var(--cp-dark-900);
--sidebar-icon: var(--text-color);

// Theme color
--theme-color: var(--card-background);
Expand Down
1 change: 1 addition & 0 deletions src/lib/styles/themes/light.scss
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
// Sidebar
--sidebar-button-background: var(--cp-light-50-45);
--sidebar-button-background-hover: var(--cp-light-50);
--sidebar-icon: var(--text-color);

// Theme color
--theme-color: var(--cp-light-accent);
Expand Down
15 changes: 15 additions & 0 deletions src/routes/(split)/components/theme-toggle-button/+page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import ThemeToggleButton from "$lib/components/ThemeToggleButton.svelte";
</script>

# Theme Toggle Button

In addition to [theming](/styling/theming) we also provide an opinionated toggle to integrate easily a theme switcher in your dapp.

```javascript
<ThemeToggleButton />
```

## Showcase

<ThemeToggleButton />
58 changes: 58 additions & 0 deletions src/tests/lib/components/ThemeToggleButton.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import ThemeToggleButton from "$lib/components/ThemeToggleButton.svelte";
import IconDarkMode from "$lib/icons/IconDarkMode.svelte";
import IconLightMode from "$lib/icons/IconLightMode.svelte";
import { themeStore } from "$lib/stores/theme.store";
import { Theme } from "$lib/types/theme";
import { fireEvent, render } from "@testing-library/svelte";
import { get } from "svelte/store";
import en from "../mocks/i18n.mock";

describe("ThemeToggleButton", () => {
it("should render a toggle button", () => {
const { container } = render(ThemeToggleButton);

const input = container.querySelector("button") as HTMLButtonElement;
expect(input).not.toBeNull();
});

it("should render an accessible toggle button", () => {
const { container } = render(ThemeToggleButton);

const input = container.querySelector("button") as HTMLButtonElement;
expect(input.getAttribute("aria-label")).toEqual(en.theme.switch_theme);
});

it("should switch theme", () => {
const { container } = render(ThemeToggleButton);

const input = container.querySelector("button") as HTMLButtonElement;

fireEvent.click(input);
expect(get(themeStore)).toEqual(Theme.LIGHT);

fireEvent.click(input);
expect(get(themeStore)).toEqual(Theme.DARK);
});

it("should render IconLightMode when theme is dark", () => {
themeStore.select(Theme.DARK);
const { container } = render(ThemeToggleButton);

const lightModeIcon = container.querySelector("svg");
expect(lightModeIcon).toBeInstanceOf(SVGSVGElement);
expect(container.innerHTML).toContain(
render(IconLightMode).container.innerHTML,
);
});

it("should render IconDarkMode when theme is light", () => {
themeStore.select(Theme.LIGHT);
const { container } = render(ThemeToggleButton);

const darkModeIcon = container.querySelector("svg");
expect(darkModeIcon).toBeInstanceOf(SVGSVGElement);
expect(container.innerHTML).toContain(
render(IconDarkMode).container.innerHTML,
);
});
});

0 comments on commit 9a16f6f

Please sign in to comment.