Skip to content
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

feat(hooks): use-theme hook #3169

Merged
merged 27 commits into from
Nov 4, 2024
Merged

feat(hooks): use-theme hook #3169

merged 27 commits into from
Nov 4, 2024

Conversation

wingkwong
Copy link
Member

@wingkwong wingkwong commented Jun 3, 2024

Closes #

📝 Description

  • to replace the outdated use-dark-mode

⛳️ Current behavior (updates)

Please describe the current behavior that you are modifying

🚀 New behavior

useTheme(ThemeProps.LIGHT):
image

useTheme(ThemeProps.DARK):
image

useTheme(ThemeProps.SYSTEM):

image

💣 Is this a breaking change (Yes/No):

📝 Additional Information

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced the useTheme hook for managing and switching between light and dark themes.
    • Enhanced theme management capabilities for improved user experience.
  • Documentation

    • Updated documentation to reflect the transition from the use-dark-mode hook to the @nextui-org/use-theme hook.
    • Clarified installation instructions and example usage for the new hook, including updated theme switching methods.
    • Added a note indicating that the "dark mode" documentation has been recently updated.

Copy link

linear bot commented Jun 3, 2024

Copy link

vercel bot commented Jun 3, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nextui-docs-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 4, 2024 8:59pm
nextui-storybook-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 4, 2024 8:59pm

Copy link

changeset-bot bot commented Jun 3, 2024

🦋 Changeset detected

Latest commit: dcb81a4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@nextui-org/use-theme Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

coderabbitai bot commented Jun 3, 2024

Warning

Rate limit exceeded

@jrgarciadev has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 45 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b7db3d3 and dcb81a4.

Walkthrough

This update introduces the use-theme hook in the @nextui-org/use-theme package, facilitating theme management and allowing for switching between light and dark themes. It includes updates to documentation reflecting the new hook's integration, transitioning from the use-dark-mode hook. The changes encompass modifications to import statements, function calls, and documentation paths to align with the new theme management approach, alongside the addition of a new test suite for the hook.

Changes

File/Path Change Summary
.changeset/light-needles-behave.md Introduced the use-theme hook for the @nextui-org/use-theme package, added ThemeProps, customTheme, and Theme types, and defined useTheme function. Updated ThemeSwitcher component and related documentation to use the new hook.
apps/docs/config/routes.json Added "updated": true to the "dark mode" section.
apps/docs/content/docs/customization/dark-mode.mdx Renamed use-dark-mode hook to useTheme and updated related imports and usage. Revised installation instructions and example code snippets.

Possibly related PRs

  • fix(docs): typos in dark mode page #3823: This PR addresses a typo in the dark mode documentation, specifically correcting "exits" to "exists," which is relevant to the theme management changes introduced in the main PR.

Suggested labels

📋 Scope : Docs

Suggested reviewers

  • tianenpang
  • winchesHe

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f3fdb7b and aab2644.

Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !pnpm-lock.yaml
Files selected for processing (8)
  • .changeset/light-needles-behave.md (1 hunks)
  • apps/docs/config/routes.json (1 hunks)
  • apps/docs/content/docs/customization/dark-mode.mdx (3 hunks)
  • packages/hooks/use-theme/README.md (1 hunks)
  • packages/hooks/use-theme/tests/use-theme.test.tsx (1 hunks)
  • packages/hooks/use-theme/package.json (1 hunks)
  • packages/hooks/use-theme/src/index.ts (1 hunks)
  • packages/hooks/use-theme/tsconfig.json (1 hunks)
Files skipped from review due to trivial changes (3)
  • .changeset/light-needles-behave.md
  • packages/hooks/use-theme/package.json
  • packages/hooks/use-theme/tsconfig.json
Additional context used
Markdownlint
packages/hooks/use-theme/README.md

32-32: Expected: 0 or 2; Actual: 1
Trailing spaces

Biome
packages/hooks/use-theme/src/index.ts

[error] 25-25: This variable implicitly has the any type.

Variable declarations without type annotation and initialization implicitly have the any type. Declare a type or initialize the variable with some value.


[error] 46-46: This hook does not specify all of its dependencies: setTheme

This dependency is not specified in the hook dependency list.

packages/hooks/use-theme/__tests__/use-theme.test.tsx

[error] 29-29: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.


[error] 12-12: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset


[error] 13-13: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset


[error] 25-25: This let declares a variable that is only assigned once.

'store' is never reassigned.

Safe fix: Use const instead.

Additional comments not posted (5)
packages/hooks/use-theme/README.md (2)

7-11: Installation and basic usage instructions are clear and well-documented.

Also applies to: 18-19, 27-28, 36-44


48-55: Contribution guidelines and licensing information are appropriately linked and clear.

packages/hooks/use-theme/src/index.ts (1)

4-11: Constants and type definitions for themes are well-structured and clear.

Also applies to: 14-15

apps/docs/content/docs/customization/dark-mode.mdx (1)

Line range hint 194-248: Documentation for using the useTheme hook is comprehensive and well-structured.

apps/docs/config/routes.json (1)

115-116: The route configuration update correctly reflects the documentation changes for dark mode.

packages/hooks/use-theme/README.md Outdated Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Outdated Show resolved Hide resolved
packages/hooks/use-theme/__tests__/use-theme.test.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between aab2644 and f91afbf.

Files selected for processing (1)
  • packages/hooks/use-theme/tests/use-theme.test.tsx (1 hunks)
Additional context used
Biome
packages/hooks/use-theme/__tests__/use-theme.test.tsx

[error] 25-25: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.


[error] 26-26: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.


[error] 12-12: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset


[error] 13-13: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

Additional comments not posted (4)
packages/hooks/use-theme/__tests__/use-theme.test.tsx (4)

43-48: This test case correctly checks the initialization behavior when no theme is stored.


50-56: This test case correctly handles initialization with a custom theme.


58-67: This test case correctly handles initialization with a theme stored in localStorage.


69-78: This test case correctly tests the functionality of setting a new theme and its effects on localStorage and the DOM.

packages/hooks/use-theme/__tests__/use-theme.test.tsx Outdated Show resolved Hide resolved
packages/hooks/use-theme/__tests__/use-theme.test.tsx Outdated Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Outdated Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (2)
packages/hooks/use-theme/src/index.ts (2)

16-16: Consider using PascalCase for type names

For consistency with TypeScript naming conventions and other type definitions in this file, consider renaming customTheme to CustomTheme.

-export type customTheme = string;
+export type CustomTheme = string;

This change would make the type naming more consistent throughout the file.


39-39: Update comment for accuracy

The current comment doesn't accurately reflect the code's behavior. Consider updating it to:

-    // return light theme if not specified
+    // if system theme is not applicable, return the defaultTheme

This change more accurately describes the fallback behavior of the initialization logic.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1723387 and a87ad5f.

📒 Files selected for processing (1)
  • packages/hooks/use-theme/src/index.ts (1 hunks)
🔇 Additional comments (1)
packages/hooks/use-theme/src/index.ts (1)

1-85: Overall assessment: Well-implemented useTheme hook with minor improvement opportunities

The useTheme hook is well-implemented, providing a robust solution for theme management. It handles various scenarios including system preference, localStorage persistence, and custom themes. The code is generally well-structured and uses React hooks appropriately.

A few minor improvements have been suggested:

  1. Consistent naming convention for types
  2. More accurate comments
  3. Optimizing useCallback dependency arrays
  4. Removing a potentially redundant useEffect

These changes would further enhance the code's clarity, consistency, and efficiency. Great job on implementing this feature!

packages/hooks/use-theme/src/index.ts Outdated Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a87ad5f and b40cbf8.

📒 Files selected for processing (1)
  • packages/hooks/use-theme/src/index.ts (1 hunks)
🔇 Additional comments (5)
packages/hooks/use-theme/src/index.ts (5)

1-13: LGTM: Imports and constants are well-defined

The imports are correct and necessary for the hook implementation. The ThemeProps constant is well-defined with clear, descriptive keys for localStorage and theme values. The use of 'as const' ensures type safety, which is a good practice.


15-21: LGTM: Type definitions are clear and flexible

The type definitions for customTheme and Theme are well-structured. The Theme type, being a union of predefined themes and customTheme, allows for both standard and custom theme usage, providing flexibility while maintaining type safety.


29-45: LGTM: Hook initialization and state management

The useTheme hook is well-implemented with proper initialization and state management. It correctly handles the default theme, stored theme, and system preference.


76-85: LGTM: System preference listener and hook return

The implementation of the system preference listener and the hook's return value is correct and well-structured.


47-63: 🛠️ Refactor suggestion

Update dependency array in setTheme useCallback

The setTheme function uses the MEDIA constant, but it's not included in the dependency array. Although MEDIA is unlikely to change, including it would make the code more robust.

Consider updating the dependency array:

-  [theme],
+  [theme, MEDIA],

Likely invalid or redundant comment.

packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
apps/docs/content/docs/customization/dark-mode.mdx (1)

218-227: LGTM with minor formatting suggestion

The code example correctly demonstrates the usage of the new useTheme hook from @nextui-org/use-theme. The theme is properly applied to the main element's className.

Consider adjusting the indentation of the JSX in the return statement for consistency:

 return (
   <main className={`${theme} text-foreground bg-background`}>
-      <App />
+    <App />
   </main>
 )
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between b40cbf8 and 721f279.

📒 Files selected for processing (1)
  • apps/docs/content/docs/customization/dark-mode.mdx (3 hunks)
🔇 Additional comments (4)
apps/docs/content/docs/customization/dark-mode.mdx (4)

194-197: LGTM: Updated section title and introduction

The changes accurately reflect the transition from use-dark-mode to @nextui-org/use-theme. The new hook is correctly introduced and linked to its GitHub repository.


201-209: LGTM: Updated installation instructions

The installation instructions have been correctly updated to use @nextui-org/use-theme instead of use-dark-mode. The package manager commands are accurate for npm, yarn, and pnpm.


239-250: LGTM: Updated ThemeSwitcher component

The ThemeSwitcher component has been correctly updated to use the useTheme hook from @nextui-org/use-theme. The theme switching logic now uses setTheme('light') and setTheme('dark'), which is consistent with the new API.


254-256: LGTM: Retained important note about custom themes

The note about using custom theme names has been correctly retained. This information is still relevant and important for users who want to create and use custom themes with the new useTheme hook.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
packages/hooks/use-theme/src/index.ts (1)

1-21: LGTM! Consider using as const for better type inference.

The imports, constant definitions, and type declarations are well-structured and provide a solid foundation for the theme management system. The use of a union type for Theme allows for both predefined and custom themes, which is flexible.

For improved type safety, consider using as const assertion for ThemeProps:

export const ThemeProps = {
  KEY: "nextui-theme",
  LIGHT: "light",
  DARK: "dark",
  SYSTEM: "system",
} as const;

This will ensure that the properties are treated as literal types rather than string types, providing better type inference throughout the codebase.

packages/hooks/use-theme/__tests__/use-theme.test.tsx (2)

27-43: Consider using 'const' for the store object

The localStorageMock implementation looks good overall. However, you could improve it slightly by using 'const' instead of 'let' for the store object, as it's never reassigned.

- let store: {[key: string]: string} = {};
+ const store: {[key: string]: string} = {};

This change would make the code more robust by preventing accidental reassignment of the store object.


54-146: LGTM: Comprehensive test coverage with a suggestion for improvement

The test cases provide excellent coverage for the useTheme hook, including initialization, localStorage interaction, and theme changes. The tests are well-structured and focus on specific behaviors.

Consider adding a test case for the theme change listener functionality. This would ensure that the hook properly responds to system theme changes. Here's a suggested structure:

it("should update theme when system preference changes", () => {
  let mediaQueryList;
  Object.defineProperty(window, "matchMedia", {
    writable: true,
    value: jest.fn().mockImplementation((query) => {
      mediaQueryList = {
        matches: false,
        media: query,
        onchange: null,
        addEventListener: jest.fn((_, handler) => handler({ matches: true })),
        removeEventListener: jest.fn(),
        dispatchEvent: jest.fn(),
      };
      return mediaQueryList;
    }),
  });

  const { getByTestId } = render(<TestComponent />);
  act(() => {
    getByText("Set System").click();
  });

  expect(getByTestId("theme-display").textContent).toBe(ThemeProps.SYSTEM);
  expect(document.documentElement.classList.contains(ThemeProps.LIGHT)).toBe(true);

  act(() => {
    mediaQueryList.addEventListener.mock.calls[0][1]({ matches: true });
  });

  expect(document.documentElement.classList.contains(ThemeProps.DARK)).toBe(true);
});

This test would simulate a change in the system's color scheme preference and verify that the hook responds correctly.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 721f279 and b7db3d3.

📒 Files selected for processing (2)
  • packages/hooks/use-theme/tests/use-theme.test.tsx (1 hunks)
  • packages/hooks/use-theme/src/index.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (4)
packages/hooks/use-theme/src/index.ts (1)

84-85: LGTM! Clear and concise hook return value.

The hook's return value is well-structured, providing both the current theme and the function to update it. This follows common React hook patterns and provides a clean API for components to interact with the theme management system.

packages/hooks/use-theme/__tests__/use-theme.test.tsx (3)

1-25: LGTM: Imports and TestComponent are well-structured

The imports are appropriate for the test file, and the TestComponent is well-implemented using the useTheme hook. The component provides buttons to change themes, which will be useful for testing different scenarios.


45-53: LGTM: Test suite setup is well-structured

The test suite setup is well-implemented. The use of beforeEach to clear mocks, localStorage, and reset the document class ensures a clean state for each test, which is a good practice.


1-147: Overall: Excellent test file with minor suggestions for improvement

This test file for the useTheme hook is well-structured, comprehensive, and follows good testing practices. It covers various scenarios including initialization, localStorage interaction, and theme changes. The TestComponent and localStorageMock are well-implemented, and the test cases are clear and focused.

The suggestions for improvement (using 'const' in localStorageMock and adding a test for theme change listener) are minor and would further enhance the already strong test suite.

Great job on creating a robust set of tests for the useTheme hook!

packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
packages/hooks/use-theme/src/index.ts Show resolved Hide resolved
Copy link
Member

@ryo-manba ryo-manba left a comment

Choose a reason for hiding this comment

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

LGTM! Nice job.

@jrgarciadev jrgarciadev merged commit ad7e261 into canary Nov 4, 2024
6 of 8 checks passed
@jrgarciadev jrgarciadev deleted the feat/eng-855 branch November 4, 2024 20:53
ryo-manba pushed a commit that referenced this pull request Nov 6, 2024
* feat(docs): update dark mode content

* feat(hooks): @nextui-org/use-theme

* chore(docs): revise ThemeSwitcher code

* refactor(hooks): simplify useTheme and support custom theme names

* feat(hooks): add use-theme test cases

* feat(changeset): add changeset

* refactor(hooks): make localStorageMock globally and clear before each test

* fix(docs): typo

* fix(hooks): coderabbitai comments

* chore(hooks): remove unnecessary +

* chore(changeset): change to minor

* feat(hooks): handle system theme

* chore(hooks): add EOL

* refactor(hooks): add default theme

* refactor(hooks): revise useTheme

* refactor(hooks): resolve pr comments

* refactor(hooks): resolve pr comments

* refactor(hooks): resolve pr comments

* refactor(hooks): remove unused theme in dependency array

* chore(docs): typos

* refactor(hooks): mark system as key for system theme

* chore: merged with canary

---------

Co-authored-by: Junior Garcia <jrgarciadev@gmail.com>
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.

4 participants