Skip to content

feat(Theme): add ThemeProvider for testing#3864

Open
sgaczol wants to merge 8 commits intomainfrom
@sgaczol/theme-provider
Open

feat(Theme): add ThemeProvider for testing#3864
sgaczol wants to merge 8 commits intomainfrom
@sgaczol/theme-provider

Conversation

@sgaczol
Copy link
Copy Markdown
Collaborator

@sgaczol sgaczol commented Apr 9, 2026

Description

Some of our shared components relied on useTheme() from react-navigation. This PR introduces ThemeProvider that allows to override this behaviour in useThemeColorPallette().

Closes https://github.com/software-mansion/react-native-screens-labs/issues/1100

Changes

  • add ThemeProvider.tsx and ThemeContext.ts
  • update useThemeColorPallette() accordingly to description above
  • move ScreensLightTheme and ScreensDarkTheme to Colors.ts as adapter/react-navigation directory is deleted
  • update theme-related components
  • add TestThemeProvider.tsx
  • update imports

After - visual documentation

iOS Android
iOSThemeProvider.mov
AndroidThemeProvider.mov

Test plan

Run TestThemeProvider and change system theme.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

@sgaczol sgaczol marked this pull request as ready for review April 9, 2026 15:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a ThemeProvider component that allows overriding the theme behavior in shared components, enabling better testing support. The changes migrate theme-related code from the adapter/react-navigation directory to a new theme-provider directory, update component imports accordingly, and add a test component demonstrating the new functionality.

Changes:

  • Introduce ThemeProvider and ThemeContext for theme override functionality
  • Refactor useThemeColorPallette() hook to support theme context override
  • Move theme definitions (ScreensLightTheme and ScreensDarkTheme) to Colors.ts
  • Update all component imports to use the new theme provider hook location
  • Add TestThemeProvider test component to demonstrate the feature

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/src/shared/styling/theme-provider/ThemeContext.ts New context definition for theme override
apps/src/shared/styling/theme-provider/ThemeProvider.tsx New provider component for wrapping app sections with specific themes
apps/src/shared/styling/theme-provider/useColorPallette.ts Refactored hook that checks context first before falling back to react-navigation theme
apps/src/shared/styling/Colors.ts Updated to include theme definitions and new text property in ColorPallette type
apps/src/shared/ThemedText.tsx Updated import and hook usage
apps/src/shared/ThemedTextInput.tsx Updated hook usage and changed border color property from border to cardBorder
apps/src/shared/ThemedView.tsx Updated import path
apps/src/shared/Alert.tsx Updated import path
apps/src/tests/IssueTestsScreen.tsx Updated import path for theme definitions
apps/src/tests/issue-tests/TestThemeProvider.tsx New test component demonstrating ThemeProvider functionality
apps/src/tests/issue-tests/index.ts Added export for new test component
apps/Example.tsx Updated import path for theme definitions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { useTheme } from '@react-navigation/native';

/**
* Requires `ThemeContext` (from react-navigation) presence.
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states that ThemeContext is from react-navigation, but it's actually a custom context defined in this codebase at ThemeContext.ts. This is misleading to developers reading this code.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, here's the latest version of that comment: 4ba07d9

/**
* Requires `ThemeContext` (from react-navigation) presence.
* However, it is possible to override theme by wrapping part of the app in `ThemeProvider` and passing desired theme as a prop.
* Use this to get whole collor pallete current theme is based on.
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in documentation comment: "collor" should be "color".

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thanks!
I've left a couple of remarks. Let's resolve them before proceeding.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component we add with this PR isn't part of the library API, nor it relates to any particular reported bug. We usually don't add "test for testing code". If testing code does not work correctly -> tests for library API won't work as expected. This is sufficient for now. Also we shouldn't mix library API tests with other testing code.

tldr; let's remove this one

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: 972d1fc

Comment on lines +131 to +151
export const ScreensLightTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
primary: LightColors.primary as string,
background: LightColors.background as string,
card: LightColors.cardBackground as string,
border: LightColors.cardBorder as string,
},
};

export const ScreensDarkTheme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
primary: DarkColors.primary as string,
background: DarkColors.background as string,
card: DarkColors.cardBackground as string,
border: DarkColors.cardBorder as string,
},
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two shouldn't be moved here, right? They are indeed adapters for react-navigation & they should stay there. I'd be best to have react-navigation code separated as it were. Don't mix it here. Our goal is to decouple ourselves from react-navigation 😄

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I restored those changes via rebase


export type ThemeName = 'light' | 'dark';

export const ThemeContext = React.createContext<ThemeName | undefined>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The theme values should be already in the payload. It's better than passing only a theme name.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: 4ba07d9

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to also pay attention to naming of theme-provider module here. It's not limited to provider. It also exposes other utilities. Simple theme would be better and not misleading.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: 972d1fc
I was also wondering if placing Theme-related types and constants in Colors.ts is a good idea or we should keep that in another file in theme directory

import { ThemeContext } from './ThemeContext';
import type { ThemeName } from './ThemeContext';
import { ColorPallette, DarkColors, LightColors } from '../Colors';
import { useTheme } from '@react-navigation/native';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't wanna import directly from react-navigation here. Go via adapter.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now the only added react-navigation import in this commit is in adapter/react-navigation/useReactNavigationTheme.ts: 5b37ce9

Comment on lines +12 to +27
export default function useThemeColorPallette(): {
theme: ThemeName;
colors: ColorPallette;
} {
const navigationTheme = useTheme();
let theme: ThemeName = navigationTheme.dark ? 'dark' : 'light';

const providedTheme = React.useContext(ThemeContext);
if (providedTheme !== undefined) {
theme = providedTheme;
}
return {
theme: theme,
colors: theme === 'dark' ? DarkColors : LightColors,
};
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC we described it a bit differently during the meeting.

  1. Our theme context should be prioritised.
  2. We should use react-navigation theme only if our context is not present.
  3. As you can not call the hooks conditionally, then in adapter we should add our custom implementation of react-navigation#useTheme, e.g. useReactNavigationTheme and use it here. The difference here would be that our implementation wouldn't crash, so it'd be safe to use unconditionally.

Copy link
Copy Markdown
Collaborator Author

@sgaczol sgaczol Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in 4646e59
also updated useReactNavigationTheme after that 5b37ce9

sgaczol added 8 commits April 10, 2026 14:36
Replace direct useTheme (react-navigation) usage in themed components with useThemeColorPallette hook that supports local overrides via new ThemeProvider. Add text color to ColorPallette type and light/dark palettes. Remove old adapter/react-navigation/useColorPallette in favor of new theme-provider module.
it returns theme based on useTheme
if useTheme throws an error, it is caught, a proper message is logged and DefaultTheme is returned
…, fix misspelling (pallette -> palette)

also added LightTheme and DarkTheme and Theme type to Colors.ts, also moved ThemeName into this file
useReactNavigation theme now returns undefined when useTheme throws an error
if both useTheme and ThemeProvider don't provide us a theme, then  useThemeColorPalette returns LightTheme as a default
useReactNavigationTheme works similar to useTheme though, but instead of throwing error when theme is null, it returns undefined
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants