From 97f7d67419312c50f6c39e0d6667a8996c81113e Mon Sep 17 00:00:00 2001 From: I531348 Date: Thu, 5 Dec 2024 15:47:19 +0100 Subject: [PATCH 1/5] chore(ui): initially convert themetoggle to ts --- .changeset/sharp-fishes-develop.md | 5 +++ ...component.js => ThemeToggle.component.tsx} | 43 +++++++++++-------- ...gle.stories.js => ThemeToggle.stories.tsx} | 0 ...emeToggle.test.js => ThemeToggle.test.tsx} | 24 ++++++----- .../src/components/ThemeToggle/index.js | 1 - .../src/components/ThemeToggle/index.ts | 1 + 6 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 .changeset/sharp-fishes-develop.md rename packages/ui-components/src/components/ThemeToggle/{ThemeToggle.component.js => ThemeToggle.component.tsx} (79%) rename packages/ui-components/src/components/ThemeToggle/{ThemeToggle.stories.js => ThemeToggle.stories.tsx} (100%) rename packages/ui-components/src/components/ThemeToggle/{ThemeToggle.test.js => ThemeToggle.test.tsx} (79%) delete mode 100644 packages/ui-components/src/components/ThemeToggle/index.js create mode 100644 packages/ui-components/src/components/ThemeToggle/index.ts diff --git a/.changeset/sharp-fishes-develop.md b/.changeset/sharp-fishes-develop.md new file mode 100644 index 000000000..ca616fce4 --- /dev/null +++ b/.changeset/sharp-fishes-develop.md @@ -0,0 +1,5 @@ +--- +"@cloudoperators/juno-ui-components": minor +--- + +Migrate the ThemeToggle component to TypeScript diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.js b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx similarity index 79% rename from packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.js rename to packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx index 3e8672b7e..2acc35c5f 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.js +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx @@ -1,7 +1,8 @@ import React from "react" -import PropTypes from "prop-types" -import { StyleProvider } from "../../deprecated_js/StyleProvider/index" -import { Icon } from "../../deprecated_js/Icon" + +import { StyleProvider } from "../StyleProvider/StyleProvider.component" +import { Icon } from "../Icon/Icon.component" + import "./themeToggle.scss" const toggleStyles = ` @@ -22,12 +23,33 @@ const toggleStyles = ` active:jn-bg-theme-background-lvl-4 ` +interface ThemeToggleProps { + /** Pass a custom className */ + className?: string + /** Whether the ThemeToggle is disabled */ + disabled?: boolean + /** Optional of the ThemeToggle */ + id?: string + /** Optional name attribute of the ThemeToggle */ + name?: string + /** Handler to execute when the theme is toggled */ + // eslint-disable-next-line no-unused-vars + onToggleTheme?: (...args: unknown[]) => unknown +} + /** A Toggle button to toggle Light and Dark UI Themes. Requires a StyleProvider context, which is automatically provided by the Juno AppShell. If you are not using AppShell, include a StyleProvider manually. */ -export const ThemeToggle = ({ className = "", disabled = false, id, name, onToggleTheme, ...props }) => { +export const ThemeToggle: React.FC = ({ + className = "", + disabled = false, + id, + name, + onToggleTheme, + ...props +}) => { // Consume the theme context const ThemeContext = StyleProvider.useStyles() @@ -59,16 +81,3 @@ export const ThemeToggle = ({ className = "", disabled = false, id, name, onTogg /> ) } - -ThemeToggle.propTypes = { - /** Pass a custom className */ - className: PropTypes.string, - /** Whether the ThemeToggle is disabled */ - disabled: PropTypes.bool, - /** Optional of the ThemeToggle */ - id: PropTypes.string, - /** Optional name attribute of the ThemeToggle */ - name: PropTypes.string, - /** Handler to execute when the theme is toggled */ - onToggleTheme: PropTypes.func, -} diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.js b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx similarity index 100% rename from packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.js rename to packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.js b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx similarity index 79% rename from packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.js rename to packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx index 0add639c4..02eb65fda 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.js +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx @@ -1,16 +1,18 @@ import * as React from "react" import { render, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" -import { ThemeToggle } from "./index" +import { describe, expect, test, vi } from "vitest" -const mockOnToggleTheme = jest.fn() +import { ThemeToggle } from "./ThemeToggle.component" + +const mockOnToggleTheme = vi.fn() describe("ThemeToggle", () => { let consoleWarnSpy // Set up console.warn spy beforeEach(() => { - consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {}) + consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}) }) // Restore console.warn after each test @@ -18,38 +20,38 @@ describe("ThemeToggle", () => { consoleWarnSpy.mockRestore() }) - test("render a ThemeToggle", async () => { + test("render a ThemeToggle", () => { render() expect(screen.getByRole("button")).toBeInTheDocument() expect(screen.getByRole("button")).toHaveClass("juno-theme-toggle") }) - test("renders a disabled ThemeToggle as passed", async () => { + test("renders a disabled ThemeToggle as passed", () => { render() expect(screen.getByRole("button")).toBeInTheDocument() expect(screen.getByRole("button")).toBeDisabled() }) - test("renders an id as passed", async () => { + test("renders an id as passed", () => { render() expect(screen.getByRole("button")).toBeInTheDocument() expect(screen.getByRole("button")).toHaveAttribute("id", "my-theme-toggle") }) - test("renders a name as passed", async () => { + test("renders a name as passed", () => { render() expect(screen.getByRole("button")).toBeInTheDocument() expect(screen.getByRole("button")).toHaveAttribute("name", "my-theme-toggle") }) - test("logs a console warning when no theme context is provided", async () => { + test("logs a console warning when no theme context is provided", () => { render() expect(consoleWarnSpy).toHaveBeenCalledWith( "Juno ThemeToggle requires a StyleProvider context in order to work. Use ThemeToggle in a Juno AppShell or include StyleProvider manually." ) }) - test("executes an onToggle handler when the user toggles the theme", async () => { + test("executes an onToggle handler when the user toggles the theme", () => { render() const user = userEvent.setup() const toggleButton = screen.getByRole("button") @@ -59,13 +61,13 @@ describe("ThemeToggle", () => { }) }) - test("renders a custom classNames as passed", async () => { + test("renders a custom classNames as passed", () => { render() expect(screen.getByRole("button")).toBeInTheDocument() expect(screen.getByRole("button")).toHaveClass("my-custom-class") }) - test("renders arbitrary props as passed", async () => { + test("renders arbitrary props as passed", () => { render() expect(screen.getByRole("button")).toHaveAttribute("data-lolol", "My theme toggle") }) diff --git a/packages/ui-components/src/components/ThemeToggle/index.js b/packages/ui-components/src/components/ThemeToggle/index.js deleted file mode 100644 index d2158f540..000000000 --- a/packages/ui-components/src/components/ThemeToggle/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ThemeToggle } from "./ThemeToggle.component.js" diff --git a/packages/ui-components/src/components/ThemeToggle/index.ts b/packages/ui-components/src/components/ThemeToggle/index.ts new file mode 100644 index 000000000..e9239b82d --- /dev/null +++ b/packages/ui-components/src/components/ThemeToggle/index.ts @@ -0,0 +1 @@ +export { ThemeToggle } from "./ThemeToggle.component" From edb0f6ec169974f3d5a19a6e1ad0597f750bab37 Mon Sep 17 00:00:00 2001 From: I531348 Date: Thu, 5 Dec 2024 16:11:18 +0100 Subject: [PATCH 2/5] chore(ui): fix tests and improve component --- .../ThemeToggle/ThemeToggle.component.tsx | 34 ++++++++++++------- .../ThemeToggle/ThemeToggle.stories.tsx | 1 - .../ThemeToggle/ThemeToggle.test.tsx | 13 ++++--- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx index 2acc35c5f..4fa67c6c3 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.component.tsx @@ -23,24 +23,34 @@ const toggleStyles = ` active:jn-bg-theme-background-lvl-4 ` -interface ThemeToggleProps { - /** Pass a custom className */ +export interface ThemeToggleProps { + /** + * Additional CSS classes for custom styling. + */ className?: string - /** Whether the ThemeToggle is disabled */ + /** + * If true, the ThemeToggle will be disabled and not respond to user input. + */ disabled?: boolean - /** Optional of the ThemeToggle */ + /** + * HTML id attribute for the ThemeToggle. + */ id?: string - /** Optional name attribute of the ThemeToggle */ + /** + * HTML name attribute for the ThemeToggle. + */ name?: string - /** Handler to execute when the theme is toggled */ + /** + * Callback function that is called when the theme is toggled. + */ // eslint-disable-next-line no-unused-vars - onToggleTheme?: (...args: unknown[]) => unknown + onToggleTheme?: (...args: unknown[]) => void } -/** -A Toggle button to toggle Light and Dark UI Themes. -Requires a StyleProvider context, which is automatically provided by the Juno AppShell. -If you are not using AppShell, include a StyleProvider manually. +/** + * ThemeToggle is a button component that toggles between Light and Dark UI Themes. + * This component requires a StyleProvider context to function, which is automatically provided by the Juno AppShell. + * If not using the AppShell, include a StyleProvider manually. */ export const ThemeToggle: React.FC = ({ className = "", @@ -61,7 +71,7 @@ export const ThemeToggle: React.FC = ({ } // Destructure the context, fallback - const { currentTheme: currentTheme, setThemeClass: setThemeClass } = ThemeContext || {} + const { currentTheme, setThemeClass } = ThemeContext || {} const toggleTheme = () => { const newTheme = currentTheme === "theme-dark" ? "theme-light" : "theme-dark" diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx index 08395acdd..135a06f00 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx @@ -4,7 +4,6 @@ import { ThemeToggle } from "./index" export default { title: "WIP/ThemeToggle", component: ThemeToggle, - argTypes: {}, // // Comment out decorator for now, as all StyleProviders read and write to the same key "juno-theme" in localStorage: // decorators: [ diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx index 02eb65fda..89559b2f2 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.test.tsx @@ -1,14 +1,14 @@ -import * as React from "react" +import React from "react" import { render, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" -import { describe, expect, test, vi } from "vitest" +import { describe, expect, test, beforeEach, afterEach, vi } from "vitest" import { ThemeToggle } from "./ThemeToggle.component" const mockOnToggleTheme = vi.fn() describe("ThemeToggle", () => { - let consoleWarnSpy + let consoleWarnSpy: ReturnType // Set up console.warn spy beforeEach(() => { @@ -18,6 +18,7 @@ describe("ThemeToggle", () => { // Restore console.warn after each test afterEach(() => { consoleWarnSpy.mockRestore() + mockOnToggleTheme.mockClear() }) test("render a ThemeToggle", () => { @@ -51,11 +52,13 @@ describe("ThemeToggle", () => { ) }) - test("executes an onToggle handler when the user toggles the theme", () => { + test("executes an onToggle handler when the user toggles the theme", async () => { render() const user = userEvent.setup() const toggleButton = screen.getByRole("button") - user.click(toggleButton) + + await user.click(toggleButton) + await waitFor(() => { expect(mockOnToggleTheme).toHaveBeenCalled() }) From d2d84da988e1ac4b876b32523a23d5c18e0f1df5 Mon Sep 17 00:00:00 2001 From: I531348 Date: Thu, 5 Dec 2024 16:13:11 +0100 Subject: [PATCH 3/5] chore(ui): fix tests and improve component --- .../src/components/ThemeToggle/ThemeToggle.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx index 135a06f00..a3bcc9794 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx @@ -1,4 +1,4 @@ -import { ThemeToggle } from "./index" +import { ThemeToggle } from "./ThemeToggle.component" //import { StyleProvider } from "../StyleProvider/index" export default { From 3bb7f8139ae45906f89ce7128731d6479bbb8504 Mon Sep 17 00:00:00 2001 From: I531348 Date: Thu, 5 Dec 2024 16:14:09 +0100 Subject: [PATCH 4/5] chore(ui): update src index export --- packages/ui-components/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/index.js b/packages/ui-components/src/index.js index 34b0f4ef8..4d634f0dd 100644 --- a/packages/ui-components/src/index.js +++ b/packages/ui-components/src/index.js @@ -102,7 +102,7 @@ export { Tabs } from "./components/Tabs/index.js" export { TextareaRow } from "./components/TextareaRow/index.js" export { TextInput } from "./components/TextInput/index.js" export { TextInputRow } from "./components/TextInputRow/index.js" -export { ThemeToggle } from "./components/ThemeToggle/index.js" +export { ThemeToggle } from "./components/ThemeToggle/ThemeToggle.component" export { Toast } from "./components/Toast/index.js" export { Tooltip } from "./components/Tooltip/index.js" export { TooltipContent } from "./components/TooltipContent/index.js" From 91e84fa9ab07dfbc051362f5cd994a87358a6212 Mon Sep 17 00:00:00 2001 From: I531348 Date: Thu, 5 Dec 2024 16:38:56 +0100 Subject: [PATCH 5/5] chore(ui): remove commented code --- .../ThemeToggle/ThemeToggle.stories.tsx | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx index a3bcc9794..37efbd2d1 100644 --- a/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx +++ b/packages/ui-components/src/components/ThemeToggle/ThemeToggle.stories.tsx @@ -1,24 +1,9 @@ import { ThemeToggle } from "./ThemeToggle.component" -//import { StyleProvider } from "../StyleProvider/index" export default { title: "WIP/ThemeToggle", component: ThemeToggle, argTypes: {}, - // // Comment out decorator for now, as all StyleProviders read and write to the same key "juno-theme" in localStorage: - // decorators: [ - // (Story, context) => { - // const theme = context.args.theme - // return ( - // - //
- // - //

Some content.

- //
- //
- // ) - // }, - // ], } export const Default = { @@ -26,13 +11,6 @@ export const Default = { args: {}, } -// // Comment out for now as the story will not work: The theme found in local storage and provided by the Decorator will override the prop. -// export const LightTheme = { -// args: { -// theme: "theme-light", -// }, -// } - export const Disabled = { args: { disabled: true,