-
Notifications
You must be signed in to change notification settings - Fork 39
chore: Added new useAppKitTheme hook #499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 8 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
7d078de
chore: added useAppKitTheme hook
ignaciosantise 43d1579
chore: export hook
ignaciosantise 61a4164
chore: added JSDocs to hooks, improved context check
ignaciosantise d98d04e
chore: added useAppKitTheme test
ignaciosantise cbaf886
chore: fix test
ignaciosantise 73cf2e0
chore: changed border radius of logo area
ignaciosantise da44cdd
chore: fixed useAppKitTheme tests
ignaciosantise 32f6413
Merge branch 'develop' into chore/use-theme-hook
ignaciosantise 67efd2c
chore: made themeVariables default value be an empty object again
ignaciosantise ab4eec5
chore: changed useAppKitContext error message
ignaciosantise b9bdeed
chore: added themecontroller tests
ignaciosantise d4601ce
chore: removed unnecesary undefined
ignaciosantise a731d15
chore: correctly clean themeVariables if called with setThemeVariable…
ignaciosantise 32e7408
chore: setThemeVariables change
ignaciosantise File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,13 @@ | ||
| --- | ||
| '@reown/appkit-react-native': patch | ||
| '@reown/appkit-ui-react-native': patch | ||
| '@reown/appkit-bitcoin-react-native': patch | ||
| '@reown/appkit-coinbase-react-native': patch | ||
| '@reown/appkit-common-react-native': patch | ||
| '@reown/appkit-core-react-native': patch | ||
| '@reown/appkit-ethers-react-native': patch | ||
| '@reown/appkit-solana-react-native': patch | ||
| '@reown/appkit-wagmi-react-native': patch | ||
| --- | ||
|
|
||
| chore: added useAppKitTheme hook |
This file contains hidden or 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 hidden or 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,3 @@ | ||
| module.exports = { | ||
| presets: ['module:metro-react-native-babel-preset'] | ||
| }; |
This file contains hidden or 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,2 @@ | ||
| // Import shared setup | ||
| import '@shared-jest-setup'; |
This file contains hidden or 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,9 @@ | ||
| const appkitConfig = { | ||
| ...require('../../jest.config'), | ||
| setupFilesAfterEnv: ['./jest-setup.ts'], | ||
| // Override the moduleNameMapper to use the correct path from the package | ||
| moduleNameMapper: { | ||
| '^@shared-jest-setup$': '../../jest-shared-setup.ts' | ||
| } | ||
| }; | ||
| module.exports = appkitConfig; |
This file contains hidden or 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 hidden or 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
188 changes: 188 additions & 0 deletions
188
packages/appkit/src/__tests__/hooks/useAppKitTheme.test.tsx
This file contains hidden or 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,188 @@ | ||
| import { renderHook, act } from '@testing-library/react-native'; | ||
| import React from 'react'; | ||
| import { useAppKitTheme } from '../../hooks/useAppKitTheme'; | ||
| import { ThemeController } from '@reown/appkit-core-react-native'; | ||
| import { type AppKitContextType, AppKitContext } from '../../AppKitContext'; | ||
| import type { AppKit } from '../../AppKit'; | ||
|
|
||
| // Mock valtio | ||
| jest.mock('valtio', () => ({ | ||
| useSnapshot: jest.fn(state => state) | ||
| })); | ||
|
|
||
| // Mock ThemeController | ||
| jest.mock('@reown/appkit-core-react-native', () => ({ | ||
| ThemeController: { | ||
| state: { | ||
| themeMode: undefined, | ||
| themeVariables: undefined | ||
| }, | ||
| setThemeMode: jest.fn(), | ||
| setThemeVariables: jest.fn() | ||
| } | ||
| })); | ||
|
|
||
| describe('useAppKitTheme', () => { | ||
| const mockAppKit = {} as AppKit; | ||
|
|
||
| const wrapper = ({ children }: { children: React.ReactNode }) => { | ||
| const contextValue: AppKitContextType = { appKit: mockAppKit }; | ||
|
|
||
| return <AppKitContext.Provider value={contextValue}>{children}</AppKitContext.Provider>; | ||
| }; | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| // Reset ThemeController state | ||
| ThemeController.state = { | ||
| themeMode: undefined, | ||
| themeVariables: undefined | ||
| }; | ||
| }); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| it('should throw error when used outside AppKitProvider', () => { | ||
| // Suppress console.error for this test | ||
| const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); | ||
|
|
||
| expect(() => { | ||
| renderHook(() => useAppKitTheme()); | ||
| }).toThrow('AppKit instance is not yet available in context.'); | ||
|
|
||
| consoleSpy.mockRestore(); | ||
| }); | ||
|
|
||
| it('should return initial theme state', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| expect(result.current.themeMode).toBeUndefined(); | ||
| expect(result.current.themeVariables).toBeUndefined(); | ||
| }); | ||
|
|
||
| it('should return dark theme mode when set', () => { | ||
| ThemeController.state = { | ||
| themeMode: 'dark', | ||
| themeVariables: undefined | ||
| }; | ||
|
|
||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| expect(result.current.themeMode).toBe('dark'); | ||
| }); | ||
|
|
||
| it('should return light theme mode when set', () => { | ||
| ThemeController.state = { | ||
| themeMode: 'light', | ||
| themeVariables: undefined | ||
| }; | ||
|
|
||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| expect(result.current.themeMode).toBe('light'); | ||
| }); | ||
|
|
||
| it('should return theme variables when set', () => { | ||
| const themeVariables = { accent: '#00BB7F' }; | ||
| ThemeController.state = { | ||
| themeMode: undefined, | ||
| themeVariables | ||
| }; | ||
|
|
||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| expect(result.current.themeVariables).toEqual(themeVariables); | ||
| }); | ||
|
|
||
| it('should call ThemeController.setThemeMode when setThemeMode is called', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| act(() => { | ||
| result.current.setThemeMode('dark'); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeMode).toHaveBeenCalledWith('dark'); | ||
| }); | ||
|
|
||
| it('should call ThemeController.setThemeMode with undefined', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| act(() => { | ||
| result.current.setThemeMode(undefined); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeMode).toHaveBeenCalledWith(undefined); | ||
| }); | ||
|
|
||
| it('should call ThemeController.setThemeVariables when setThemeVariables is called', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
| const themeVariables = { accent: '#FF5733' }; | ||
|
|
||
| act(() => { | ||
| result.current.setThemeVariables(themeVariables); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeVariables).toHaveBeenCalledWith(themeVariables); | ||
| }); | ||
|
|
||
| it('should call ThemeController.setThemeVariables with undefined', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| act(() => { | ||
| result.current.setThemeVariables(undefined); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeVariables).toHaveBeenCalledWith(undefined); | ||
| }); | ||
|
|
||
| it('should return stable function references', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| const firstSetThemeMode = result.current.setThemeMode; | ||
| const firstSetThemeVariables = result.current.setThemeVariables; | ||
|
|
||
| // Functions should be stable (same reference) | ||
| expect(result.current.setThemeMode).toBe(firstSetThemeMode); | ||
| expect(result.current.setThemeVariables).toBe(firstSetThemeVariables); | ||
| }); | ||
|
|
||
| it('should update theme mode and variables together', () => { | ||
| ThemeController.state = { | ||
| themeMode: 'dark', | ||
| themeVariables: { accent: '#00BB7F' } | ||
| }; | ||
|
|
||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| expect(result.current.themeMode).toBe('dark'); | ||
| expect(result.current.themeVariables).toEqual({ accent: '#00BB7F' }); | ||
| }); | ||
|
|
||
| it('should handle multiple setThemeMode calls', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
|
|
||
| act(() => { | ||
| result.current.setThemeMode('dark'); | ||
| result.current.setThemeMode('light'); | ||
| result.current.setThemeMode(undefined); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeMode).toHaveBeenCalledTimes(3); | ||
| expect(ThemeController.setThemeMode).toHaveBeenNthCalledWith(1, 'dark'); | ||
| expect(ThemeController.setThemeMode).toHaveBeenNthCalledWith(2, 'light'); | ||
| expect(ThemeController.setThemeMode).toHaveBeenNthCalledWith(3, undefined); | ||
| }); | ||
|
|
||
| it('should handle multiple setThemeVariables calls', () => { | ||
| const { result } = renderHook(() => useAppKitTheme(), { wrapper }); | ||
| const variables1 = { accent: '#00BB7F' }; | ||
| const variables2 = { accent: '#FF5733' }; | ||
|
|
||
| act(() => { | ||
| result.current.setThemeVariables(variables1); | ||
| result.current.setThemeVariables(variables2); | ||
| }); | ||
|
|
||
| expect(ThemeController.setThemeVariables).toHaveBeenCalledTimes(2); | ||
| expect(ThemeController.setThemeVariables).toHaveBeenNthCalledWith(1, variables1); | ||
| expect(ThemeController.setThemeVariables).toHaveBeenNthCalledWith(2, variables2); | ||
| }); | ||
| }); | ||
This file contains hidden or 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 hidden or 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 hidden or 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,43 @@ | ||
| import { useContext } from 'react'; | ||
|
|
||
| import { AppKitContext, type AppKitContextType } from '../AppKitContext'; | ||
|
|
||
| /** | ||
| * Hook to access the AppKit context | ||
| * | ||
| * @remarks | ||
| * This is an internal hook used by other AppKit hooks to ensure they're used within | ||
| * the AppKitProvider. You typically don't need to use this hook directly - use the | ||
| * higher-level hooks like `useAppKit`, `useAccount`, `useAppKitTheme`, etc. instead. | ||
| * | ||
| * @returns {AppKitContextType} The AppKit context containing the AppKit instance | ||
| * | ||
| * @throws {Error} If used outside of an AppKitProvider | ||
| * @throws {Error} If the AppKit instance is not yet available in context | ||
| * | ||
| * @internal | ||
| * | ||
| * @example | ||
| * ```tsx | ||
| * // This is typically used internally by other hooks | ||
| * function MyCustomHook() { | ||
| * const context = useAppKitContext(); | ||
| * // Use context.appKit... | ||
| * } | ||
| * ``` | ||
| */ | ||
|
|
||
| export const useAppKitContext = (): AppKitContextType => { | ||
| const context = useContext(AppKitContext); | ||
|
|
||
| if (context === undefined) { | ||
| throw new Error('useAppKit must be used within an AppKitProvider'); | ||
ignaciosantise marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (!context.appKit) { | ||
| // This might happen if the provider is rendered before AppKit is initialized | ||
| throw new Error('AppKit instance is not yet available in context.'); | ||
| } | ||
|
|
||
| return context; | ||
| }; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.