-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@captn/react): add theme and language hooks
- Loading branch information
Showing
7 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { USER_LANGUAGE_KEY } from "@captn/utils/constants"; | ||
import { IPCHandlers } from "@captn/utils/types"; | ||
import { renderHook, act } from "@testing-library/react"; | ||
|
||
import { useLanguage } from "../use-language"; | ||
|
||
// Mock the IPC event system | ||
const mockOn = jest.fn(); | ||
const mockUnsubscribe = jest.fn(); | ||
window.ipc = { | ||
on: jest.fn((key, callback) => { | ||
mockOn(key, callback); | ||
return mockUnsubscribe; | ||
}), | ||
} as unknown as IPCHandlers; | ||
|
||
describe("useLanguage", () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it("should register and unregister the language change listener", () => { | ||
const { unmount } = renderHook(() => useLanguage(jest.fn())); | ||
|
||
// Check that the ipc.on was called correctly | ||
expect(window.ipc.on).toHaveBeenCalledWith(USER_LANGUAGE_KEY, expect.any(Function)); | ||
expect(mockOn).toHaveBeenCalledWith(USER_LANGUAGE_KEY, expect.any(Function)); | ||
|
||
// Unmount the hook and check if cleanup is performed correctly | ||
unmount(); | ||
expect(mockUnsubscribe).toHaveBeenCalled(); | ||
}); | ||
|
||
it("should handle callback updates correctly", () => { | ||
const mockCallback1 = jest.fn(); | ||
const mockCallback2 = jest.fn(); | ||
|
||
const { rerender } = renderHook(({ callback }) => useLanguage(callback), { | ||
initialProps: { callback: mockCallback1 }, | ||
}); | ||
|
||
// Simulate a language change | ||
const language = "en"; | ||
act(() => { | ||
const callback = mockOn.mock.calls[0][1]; | ||
callback(language); | ||
}); | ||
expect(mockCallback1).toHaveBeenCalledWith(language); | ||
|
||
// Update the callback and simulate another language change | ||
rerender({ callback: mockCallback2 }); | ||
const newLanguage = "de"; | ||
act(() => { | ||
const callback = mockOn.mock.calls[0][1]; // The callback should remain the same reference | ||
callback(newLanguage); | ||
}); | ||
expect(mockCallback2).toHaveBeenCalledWith(newLanguage); | ||
expect(mockCallback1).not.toHaveBeenCalledWith(newLanguage); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { USER_THEME_KEY } from "@captn/utils/constants"; | ||
import { IPCHandlers } from "@captn/utils/types"; | ||
import { renderHook, act } from "@testing-library/react"; | ||
|
||
import { useTheme } from "../use-theme"; | ||
|
||
// Mock the IPC event system | ||
const mockOn = jest.fn(); | ||
const mockUnsubscribe = jest.fn(); | ||
window.ipc = { | ||
on: jest.fn((key, callback) => { | ||
mockOn(key, callback); | ||
return mockUnsubscribe; | ||
}), | ||
} as unknown as IPCHandlers; | ||
|
||
describe("useTheme", () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it("registers and unregisters the theme change listener", () => { | ||
const { unmount } = renderHook(() => useTheme(jest.fn())); | ||
|
||
// Expect that ipc.on was called with USER_THEME_KEY | ||
expect(window.ipc.on).toHaveBeenCalledWith(USER_THEME_KEY, expect.any(Function)); | ||
|
||
// Unmount the hook to simulate the component unmounting | ||
unmount(); | ||
|
||
// Expect that the unsubscribe function was called | ||
expect(mockUnsubscribe).toHaveBeenCalled(); | ||
}); | ||
|
||
it("handles theme updates correctly", () => { | ||
const callback = jest.fn(); | ||
const { rerender } = renderHook(({ callback }) => useTheme(callback), { | ||
initialProps: { callback }, | ||
}); | ||
|
||
// Simulate an IPC event that changes the theme | ||
act(() => { | ||
const themeCallback = mockOn.mock.calls[0][1]; | ||
themeCallback("dark"); | ||
}); | ||
|
||
// Check that the callback was called with the right argument | ||
expect(callback).toHaveBeenCalledWith("dark"); | ||
|
||
// Change the callback function | ||
const newCallback = jest.fn(); | ||
rerender({ callback: newCallback }); | ||
|
||
// Simulate another IPC event | ||
act(() => { | ||
const themeCallback = mockOn.mock.calls[0][1]; | ||
themeCallback("light"); | ||
}); | ||
|
||
// Verify the new callback is called | ||
expect(newCallback).toHaveBeenCalledWith("light"); | ||
expect(callback).not.toHaveBeenCalledWith("light"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
export * from "./types"; | ||
export * from "./use-sdk"; | ||
export * from "./use-captain-action"; | ||
export * from "./use-language"; | ||
export * from "./use-object"; | ||
export * from "./use-required-downloads"; | ||
export * from "./use-resettable-state"; | ||
export * from "./use-save-image"; | ||
export * from "./use-sdk"; | ||
export * from "./use-text-to-image"; | ||
export * from "./use-theme"; | ||
export * from "./use-unload"; | ||
export * from "./use-vector-store"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { USER_LANGUAGE_KEY } from "@captn/utils/constants"; | ||
import { useEffect, useRef } from "react"; | ||
|
||
/** | ||
* A custom React hook that subscribes to language changes within the application and executes a callback | ||
* function when the language changes. This hook ensures that the callback function has the most current | ||
* reference through the use of a ref, and it handles cleanup by unsubscribing from the event listener | ||
* when the component unmounts. | ||
* | ||
* The hook listens for changes to the user's language setting through an IPC event identified by | ||
* `USER_LANGUAGE_KEY`. It is designed for use in environments where language settings can change | ||
* dynamically and need to be responded to in real-time, such as in desktop applications built with Electron. | ||
* | ||
* @param {Function} callback - A callback function that is called with the new language string as its | ||
* argument whenever the language setting changes. This function can perform any actions needed in | ||
* response to a language change, such as updating state or UI elements. | ||
* | ||
* Usage: | ||
* This hook is used in components that need to react to changes in the user's language setting. It | ||
* abstracts the complexity of subscribing to and handling IPC events, making it easier to implement | ||
* responsive, i18n-aware components. | ||
*/ | ||
export function useLanguage(callback: (language: string) => void) { | ||
const callbackReference = useRef(callback); | ||
|
||
useEffect(() => { | ||
callbackReference.current = callback; | ||
}, [callback]); | ||
|
||
useEffect(() => { | ||
const unsubscribe = window.ipc?.on(USER_LANGUAGE_KEY, (language: string) => { | ||
callbackReference.current(language); | ||
}); | ||
return () => { | ||
if (unsubscribe) { | ||
unsubscribe(); | ||
} | ||
}; | ||
}, []); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { USER_THEME_KEY } from "@captn/utils/constants"; | ||
import { useEffect, useRef } from "react"; | ||
|
||
/** | ||
* A custom React hook that subscribes to theme changes within the application and executes a callback | ||
* function when the theme changes. This hook manages the lifecycle of theme-related event listeners, | ||
* ensuring that the most current callback is used and properly cleaned up when the component unmounts | ||
* or the callback changes. | ||
* | ||
* The hook listens for theme changes through an IPC event defined by `USER_THEME_KEY`. It supports | ||
* themes specified as "light", "dark", or "system", providing flexibility in responding to changes | ||
* in the user's preferred theme settings in a dynamic environment, such as an Electron application. | ||
* | ||
* @param {Function} callback - A callback function that is called with the new theme setting ("light", | ||
* "dark", or "system") as its argument whenever the theme setting changes. This function can perform | ||
* any actions needed in response to a theme change, such as updating state or UI elements to reflect | ||
* the new theme. | ||
* | ||
* Usage: | ||
* This hook is typically used in components that need to react to changes in the user's theme | ||
* settings, ensuring that the application's UI is always in sync with the user's preferences. It | ||
* abstracts the complexity of subscribing to and handling IPC events, making it easier to implement | ||
* theme-aware components. | ||
*/ | ||
export function useTheme(callback: (theme: "light" | "dark" | "system") => void) { | ||
const callbackReference = useRef(callback); | ||
|
||
useEffect(() => { | ||
callbackReference.current = callback; | ||
}, [callback]); | ||
|
||
useEffect(() => { | ||
const unsubscribe = window.ipc?.on( | ||
USER_THEME_KEY, | ||
(theme?: "light" | "dark" | "system") => { | ||
if (theme) { | ||
callbackReference.current(theme); | ||
} | ||
} | ||
); | ||
return () => { | ||
if (unsubscribe) { | ||
unsubscribe(); | ||
} | ||
}; | ||
}, []); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters