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

Drawer Component #3986

Merged
merged 2 commits into from
Nov 4, 2024
Merged

Drawer Component #3986

merged 2 commits into from
Nov 4, 2024

Conversation

jrgarciadev
Copy link
Member

@jrgarciadev jrgarciadev commented Nov 4, 2024

Base PR: #2223

📝 Description

⛳️ Current behavior (updates)

🚀 New behavior

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

📝 Additional Information

Summary by CodeRabbit

  • New Features

    • Introduced a new "Drawer" component with customizable options for backdrop, size, and placement.
    • Added several new and updated components in the documentation, enhancing user experience.
    • Comprehensive documentation for the "Drawer" component, including installation and usage guidelines.
  • Bug Fixes

    • Improved accessibility features and ARIA attributes for the "Drawer" component.
  • Tests

    • Added unit tests for the "Drawer" component to ensure functionality and accessibility compliance.
  • Documentation

    • Created README and detailed documentation for the "Drawer" component and its usage.

1111mp and others added 2 commits January 10, 2024 18:19
Signed-off-by: The1111mp <The1111mp@outlook.com>
Copy link

changeset-bot bot commented Nov 4, 2024

⚠️ No Changeset found

Latest commit: 884fc4c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

Copy link

vercel bot commented Nov 4, 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:14pm
nextui-storybook-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 4, 2024 8:14pm

Copy link
Contributor

coderabbitai bot commented Nov 4, 2024

Walkthrough

The pull request introduces a new Drawer component to the NextUI library, along with comprehensive documentation and multiple supporting components. Key changes include the addition of new routes in the documentation, new React components for the Drawer interface, and unit tests for functionality verification. The Drawer component features various configurations, such as size and placement, and integrates with existing modal functionality. The changes also include updates to the theme and exports to facilitate the use of the new Drawer component within the library.

Changes

File Path Change Summary
apps/docs/config/routes.json Added new route for "Drawer" component; marked several components as new or updated.
apps/docs/content/components/drawer/backdrop.ts Introduced new components: MailIcon, LockIcon, and App for the drawer backdrop interface.
apps/docs/content/components/drawer/custom-motion.ts Added new App component for customizable drawer motion animations.
apps/docs/content/components/drawer/form.ts Created MailIcon, LockIcon, and App components for the login form in the drawer.
apps/docs/content/components/drawer/index.ts Added drawerContent constant to export related drawer modules.
apps/docs/content/components/drawer/non-dismissable.ts Introduced App component for a non-dismissable drawer interface.
apps/docs/content/components/drawer/placement.ts Created App component to manage drawer placement options.
apps/docs/content/components/drawer/sizes.ts Added App component to manage drawer size options.
apps/docs/content/components/drawer/usage.ts Introduced App component to demonstrate drawer usage.
apps/docs/content/components/index.ts Added export for the drawer component.
apps/docs/content/docs/components/drawer.mdx Created documentation for the Drawer component, detailing usage and properties.
packages/components/drawer/README.md Added README for the @nextui-org/drawer component.
packages/components/drawer/tests/drawer.test.tsx Introduced unit tests for the Drawer component and its subcomponents.
packages/components/drawer/package.json Created package.json for the @nextui-org/drawer package.
packages/components/drawer/src/drawer.tsx Introduced Drawer component utilizing modal functionality.
packages/components/drawer/src/index.ts Exported various drawer-related components and hooks.
packages/components/drawer/src/use-drawer.ts Created custom hook useDrawer for managing drawer behavior and properties.
packages/components/drawer/stories/drawer.stories.tsx Added Storybook configuration for the Drawer component.
packages/components/drawer/tsconfig.json Introduced TypeScript configuration for the drawer package.
packages/components/drawer/tsup.config.ts Created tsup configuration for building the drawer package.
packages/core/react/package.json Added dependency for @nextui-org/drawer.
packages/core/react/src/index.ts Exported @nextui-org/drawer module.
packages/core/theme/src/components/alert.ts Updated alert component class definitions for consistency.
packages/core/theme/src/components/drawer.ts Introduced Tailwind Variants component for the drawer.
packages/core/theme/src/components/index.ts Added export for the drawer component.
packages/core/theme/src/utils/variants.ts Modified color values for flat variants in the theme.

Assessment against linked issues

Objective Addressed Explanation
Need a Drawer Component (#[2620])
Drawer Component (#[2376])

Possibly related PRs

  • fix(theme): update flat variant text colors to be accessible #3738: This PR addresses accessibility issues related to text color contrast in the NextUI theme, which may indirectly relate to the overall documentation updates in the main PR that includes new components and their documentation, enhancing user experience and accessibility.

Suggested reviewers

  • wingkwong

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: 15

🧹 Outside diff range and nitpick comments (38)
apps/docs/content/components/drawer/index.ts (1)

1-17: LGTM! Consider adding JSDoc documentation.

The file structure is well-organized, with a clear separation of concerns and consistent naming conventions. The exports are logically grouped and follow a natural progression from basic usage to advanced features.

Consider adding JSDoc documentation to describe the purpose of the drawerContent export:

+/**
+ * Documentation content for the Drawer component.
+ * Includes examples for basic usage, sizing, placement, forms,
+ * backdrop variations, and custom animations.
+ */
export const drawerContent = {
packages/components/drawer/src/drawer.tsx (1)

10-18: Consider architectural improvements for robustness and performance.

While the current implementation is clean, consider these improvements:

  1. Wrap the Modal in an error boundary to handle potential rendering failures gracefully
  2. Consider implementing memo for performance optimization if the drawer content is complex
  3. Ensure proper cleanup in the useDrawer hook for animations and event listeners

Would you like me to provide example implementations for any of these suggestions?

apps/docs/content/components/drawer/non-dismissable.ts (2)

1-4: Consider adding TypeScript types for better documentation.

Since this is a documentation example, adding TypeScript types would provide better guidance for users implementing the component.

-const App = `import {Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure} from "@nextui-org/react";
+const App = `import {Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, Button, useDisclosure} from "@nextui-org/react";
+
+type Props = {
+  // Add any props if needed
+};
 
-export default function App() {
+export default function App(props: Props) {

15-41: Enhance documentation example with meaningful content.

Replace the Lorem ipsum text with content that better demonstrates the drawer's purpose and common use cases. This would provide more value to developers learning how to implement the component.

                <p>
                  Click the close button or action button to close the drawer.
                  Clicking outside or pressing the escape key won't close it.
                </p>
                <p> 
-                Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-                Nullam pulvinar risus non risus hendrerit venenatis.
-                Pellentesque sit amet hendrerit risus, sed porttitor quam.
+                This pattern is useful for important forms or processes that
+                shouldn't be accidentally dismissed. For example, when users
+                are in the middle of a critical workflow or data entry task.
                </p>
packages/core/theme/src/components/drawer.ts (3)

5-19: Update JSDoc documentation to reflect Drawer component

The current documentation refers to a "Modal" component and provides a basic example. Consider enhancing it to:

  1. Correctly reference the "Drawer" component
  2. Demonstrate the usage of size and placement variants
  3. Include examples of common use cases
 /**
- * Modal **Tailwind Variants** component
+ * Drawer **Tailwind Variants** component
  *
  * @example
  * ```js
- * const {base} = drawer({...})
+ * const {base} = drawer({
+ *   size: "md",
+ *   placement: "right"
+ * })
  *
  * <div>
  *    <button>Open Drawer</button>
  *    <div className={base()}>
  *       Drawer Content
  *    </div>
  * </div>
  * ```
  */

25-56: Consider using relative units for better responsiveness

The current implementation uses fixed max-height values which might not scale well across different viewport sizes. Consider:

  1. Using viewport-relative units (vh/dvh) for better adaptability
  2. Adding responsive variants for different breakpoints
 size: {
   xs: {
-    base: "max-w-xs max-h-[20rem]",
+    base: "max-w-xs max-h-[30dvh]",
   },
   sm: {
-    base: "max-w-sm max-h-[24rem]",
+    base: "max-w-sm max-h-[40dvh]",
   },
   // ... adjust other sizes similarly

57-70: Add transition properties for smooth animations

The placement variants handle positioning well, but adding transition properties would improve the user experience with smooth animations.

 placement: {
   top: {
-    base: "inset-x-0 top-0 max-w-[none] rounded-t-none",
+    base: "inset-x-0 top-0 max-w-[none] rounded-t-none transition-transform duration-300",
   },
   // Apply similar changes to other placement variants
apps/docs/content/components/drawer/custom-motion.ts (3)

9-26: Consider improving animation configuration

  1. The exit animation slides to the right (x: 100) which might look unnatural for a drawer. Consider sliding back to the original position instead.
  2. The motion configuration could be extracted for reusability.

Consider applying these improvements:

+const drawerMotion = {
+  variants: {
+    enter: {
+      opacity: 1,
+      x: 0,
+      duration: 0.3,
+    },
+    exit: {
-      x: 100,
+      x: -100, // Slide back to original position
+      opacity: 0,
+      duration: 0.3,
+    },
+  },
+};

 <Drawer 
   isOpen={isOpen} 
   onOpenChange={onOpenChange}
-  motionProps={{
-    variants: {
-      enter: {
-        opacity: 1,
-        x: 0,
-        duration: 0.3,
-      },
-      exit: {
-        x: 100,
-        opacity: 0,
-        duration: 0.3,
-      },
-    },
-  }}
+  motionProps={drawerMotion}
 >

32-39: Replace Lorem ipsum with meaningful example content

The Lorem ipsum placeholder text should be replaced with content that demonstrates a real-world use case, making the documentation more practical and helpful for users.


57-63: Ensure consistency in file extensions

The example is exported with a .jsx extension while the actual file uses TypeScript. Consider using .tsx for consistency with the TypeScript codebase.

 const react = {
-  "/App.jsx": App,
+  "/App.tsx": App,
 };
packages/components/drawer/package.json (1)

2-4: Consider enhancing the package description

The current description is basic. Consider expanding it to highlight key features like placement options, sizes, or integration capabilities with NextUI's theming system.

-  "description": "Used to render a content that slides in from the side of the screen.",
+  "description": "A customizable drawer component that slides in from any edge of the screen, supporting multiple sizes, placements, and NextUI's theming system.",
apps/docs/content/components/drawer/sizes.ts (3)

1-5: Add type safety for size state

Consider adding proper TypeScript types for the size state to prevent potential runtime errors and improve code maintainability.

-  const [size, setSize] = React.useState('md')
+  type DrawerSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "full";
+  const DEFAULT_SIZE: DrawerSize = "md";
+  const [size, setSize] = React.useState<DrawerSize>(DEFAULT_SIZE);

7-12: Move sizes array outside component

To optimize performance, define the sizes array as a constant outside the component to prevent unnecessary recreation on each render.

+const DRAWER_SIZES = ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "full"] as const;
+
 export default function App() {
   const {isOpen, onOpen, onClose} = useDisclosure();
   const [size, setSize] = React.useState('md')
-
-  const sizes = ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "full"];

58-64: Simplify export configuration

The current export structure is unnecessarily complex for a single component.

-const react = {
-  "/App.jsx": App,
-};
-
-export default {
-  ...react,
-};
+export default {
+  "/App.jsx": App,
+};
apps/docs/content/components/drawer/usage.ts (4)

8-9: Add ARIA label for better accessibility.

The trigger button should have an aria-label to improve accessibility.

-      <Button onPress={onOpen}>Open Drawer</Button>
+      <Button onPress={onOpen} aria-label="Open drawer">Open Drawer</Button>

13-32: Replace Lorem ipsum with meaningful example content.

For documentation purposes, it would be more helpful to use content that demonstrates a real-world use case of the Drawer component, such as a settings panel or a shopping cart.


33-40: Improve Action button implementation.

The Action button needs more context:

  1. Add a more descriptive label indicating its purpose
  2. Consider adding an aria-label
  3. Consider adding loading state handling for async actions
-                <Button color="primary" onPress={onClose}>
-                  Action
-                </Button>
+                <Button 
+                  color="primary" 
+                  onPress={onClose}
+                  aria-label="Save changes"
+                >
+                  Save Changes
+                </Button>

1-55: Consider adding advanced usage examples.

While this example effectively demonstrates basic drawer usage, consider adding examples that showcase:

  1. Different drawer placements (left, right, top, bottom)
  2. Various drawer sizes
  3. Custom backdrop handling
  4. Nested drawers
  5. Form integration
  6. Custom transitions

This would help users better understand the component's full capabilities.

apps/docs/content/components/drawer/placement.ts (1)

12-59: Enhance accessibility and user experience

While the implementation is solid, consider these improvements:

  1. Add aria-label to buttons to better describe their purpose
  2. Add more descriptive labels for footer buttons
  3. Consider adding role="dialog" and aria-modal="true" to the Drawer

Here's how to improve the accessibility:

          <Button 
            key={placement} 
            onPress={() => handleOpen(placement)}
            className="capitalize"
+           aria-label={`Open drawer from ${placement}`}
          >
-               <Button color="danger" variant="light" onPress={onClose}>
-                 Close
+               <Button color="danger" variant="light" onPress={onClose} aria-label="Close drawer">
+                 Cancel
                </Button>
-               <Button color="primary" onPress={onClose}>
-                 Action
+               <Button color="primary" onPress={onClose} aria-label="Save changes">
+                 Save
                </Button>
packages/components/drawer/__tests__/drawer.test.tsx (3)

8-9: Improve the clarity of the warning suppression comment.

The comment about ref warnings could be more descriptive to explain why we're suppressing these specific warnings in the test context.

-// e.g. console.error Warning: Function components cannot be given refs.
-// Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
+// Suppress expected ref warnings during tests.
+// These warnings occur when testing ref forwarding and are expected behavior
+// since we're validating that refs are properly handled by the Drawer component.

92-109: Use userEvent instead of fireEvent for keyboard interactions.

For consistency with other tests and better simulation of real user interactions, use userEvent for keyboard events.

-    fireEvent.keyDown(drawer, {key: "Escape"});
+    const user = userEvent.setup();
+    await user.keyboard('{Escape}');

46-69: Enhance accessibility testing.

While the current ARIA attribute tests are good, consider adding tests for:

  1. Focus management (focus trap within drawer)
  2. Keyboard navigation between drawer elements
  3. Focus restoration after drawer closes

Example test for focus management:

it("should trap focus within drawer when open", async () => {
  const {getByRole, getByText} = render(
    <Drawer isOpen>
      <DrawerContent>
        <DrawerHeader>
          <button>First</button>
        </DrawerHeader>
        <DrawerBody>
          <button>Middle</button>
        </DrawerBody>
        <DrawerFooter>
          <button>Last</button>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );

  const user = userEvent.setup();
  await user.tab();
  expect(document.activeElement).toHaveTextContent("First");
  await user.tab();
  expect(document.activeElement).toHaveTextContent("Middle");
  await user.tab();
  expect(document.activeElement).toHaveTextContent("Last");
  await user.tab();
  // Should cycle back to first element
  expect(document.activeElement).toHaveTextContent("First");
});
apps/docs/content/components/drawer/form.ts (3)

60-77: Enhance form fields with validation and error states.

The email and password inputs lack validation feedback and error handling.

Add validation and error states:

 <Input
   autoFocus
+  isRequired
+  errorMessage={errors.email?.message}
   endContent={
     <MailIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
   }
   label="Email"
   placeholder="Enter your email"
   variant="bordered"
+  {...register('email', {
+    required: 'Email is required',
+    pattern: {
+      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
+      message: 'Invalid email address'
+    }
+  })}
 />

91-98: Add loading state and error handling for sign-in action.

The sign-in button lacks loading state and error handling.

Consider adding loading state and error handling:

 <DrawerFooter>
   <Button color="danger" variant="flat" onPress={onClose}>
     Close
   </Button>
-  <Button color="primary" onPress={onClose}>
+  <Button 
+    color="primary" 
+    isLoading={isLoading}
+    onPress={handleSubmit(async (data) => {
+      try {
+        setIsLoading(true);
+        await handleSignIn(data);
+        onClose();
+      } catch (error) {
+        setError(error.message);
+      } finally {
+        setIsLoading(false);
+      }
+    })}
+  >
     Sign in
   </Button>
 </DrawerFooter>

107-115: Consider restructuring the export format for better maintainability.

The current export structure using string templates and file paths might make maintenance and updates more challenging.

Consider exporting the components directly:

export const examples = {
  basic: {
    App,
    MailIcon,
    LockIcon,
  },
};
apps/docs/content/components/drawer/backdrop.ts (1)

1-39: Enhance icon components implementation.

Consider the following improvements for better maintainability and type safety:

  1. Define icons as actual components instead of template strings
  2. Add TypeScript types for props
  3. Add explicit units for width/height

Example implementation:

interface IconProps extends React.SVGProps<SVGSVGElement> {
  className?: string;
}

export const MailIcon: React.FC<IconProps> = (props) => (
  <svg
    aria-hidden="true"
    fill="none"
    focusable="false"
    height="1em"
    width="1em"
    role="presentation"
    viewBox="0 0 24 24"
    {...props}
  >
    {/* path remains the same */}
  </svg>
);
packages/core/theme/src/components/alert.ts (1)

27-34: Consider documenting the text-inherit behavior

The slots modifications look good, particularly:

  • Simplified class definitions using strings instead of arrays
  • Added text-inherit for better color inheritance
  • Standardized icon width with w-6

Consider adding a comment explaining the text-inherit behavior for future maintainers.

packages/components/drawer/stories/drawer.stories.tsx (4)

1-1: Document the reason for disabling the ESLint rule.

The ESLint rule jsx-a11y/no-autofocus is disabled globally for the file. Consider adding a comment explaining why this accessibility rule needs to be disabled, or better yet, scope it to the specific component where it's needed.


18-23: Consider using type-safe size options.

The size options could be derived from the component's type definitions to ensure they stay in sync with the actual component implementation.

-options: ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "full"],
+options: drawer.sizes,

136-166: Optimize PlacementTemplate implementation.

The current implementation could be improved:

  1. Memoize the placements array to prevent unnecessary recreations
  2. Memoize the handlePress callback to prevent unnecessary rerenders
 const PlacementTemplate = (args: DrawerProps) => {
   const [placement, setPlacement] = React.useState<DrawerProps["placement"]>("right");
   const {isOpen, onOpen, onOpenChange} = useDisclosure({defaultOpen: args.defaultOpen});

-  const handlePress = (placement: DrawerProps["placement"]) => {
+  const handlePress = React.useCallback((placement: DrawerProps["placement"]) => {
     setPlacement(placement);
     onOpen();
-  };
+  }, [onOpen]);

-  const placements = ["right", "left", "top", "bottom"] as DrawerProps["placement"][];
+  const placements = React.useMemo(() => 
+    ["right", "left", "top", "bottom"] as DrawerProps["placement"][], 
+    []
+  );

199-218: Enhance custom motion animation configuration.

The animation configuration could be improved by:

  1. Adding transform origin for better animation control
  2. Using NextUI's standard duration values for consistency
   args: {
     ...defaultProps,
     motionProps: {
       variants: {
         enter: {
           opacity: 1,
           x: 0,
-          duration: 0.3,
+          duration: 0.2,
+          transformOrigin: "right",
         },
         exit: {
           x: 100,
           opacity: 0,
-          duration: 0.3,
+          duration: 0.2,
+          transformOrigin: "right",
         },
       },
     },
   },
packages/components/modal/__tests__/modal.test.tsx (3)

Line range hint 1-29: Add TypeScript types to ModalDraggable props

The component props should be properly typed for better maintainability and type safety.

-const ModalDraggable = ({canOverflow = false, isDisabled = false}) => {
+interface ModalDraggableProps {
+  canOverflow?: boolean;
+  isDisabled?: boolean;
+}
+
+const ModalDraggable: React.FC<ModalDraggableProps> = ({
+  canOverflow = false,
+  isDisabled = false,
+}) => {

Line range hint 31-116: Add focus management tests

The current test suite lacks coverage for focus management, which is crucial for modal accessibility. Consider adding tests for:

  • Focus trap within the modal
  • Initial focus placement
  • Focus restoration when modal closes
test("should trap focus within modal", async () => {
  const user = userEvent.setup();
  const {getByRole, getByText} = render(
    <Modal isOpen>
      <ModalContent>
        <ModalHeader>Modal header</ModalHeader>
        <ModalBody>
          <button>First</button>
          <button>Last</button>
        </ModalBody>
      </ModalContent>
    </Modal>
  );
  
  const modal = getByRole("dialog");
  await user.tab();
  expect(document.activeElement).toBeInTheDocument();
  expect(modal).toContain(document.activeElement);
});

Line range hint 118-191: Enhance draggable modal test coverage

The draggable modal tests could be improved in several areas:

  1. Add mouse event tests for desktop drag interactions
  2. Consider DPI scaling in viewport calculations
  3. Test drag constraints and boundaries
test("should handle mouse drag events on desktop", () => {
  jest.spyOn(document.documentElement, "clientWidth", "get")
    .mockImplementation(() => 1920);
  
  const wrapper = render(<ModalDraggable />);
  const modalHeader = wrapper.getByText("Modal header");
  
  fireEvent.mouseDown(modalHeader, {clientX: 0, clientY: 0});
  fireEvent.mouseMove(modalHeader, {clientX: 100, clientY: 50});
  fireEvent.mouseUp(modalHeader);
  
  const modal = wrapper.getByRole("dialog");
  expect(modal.style.transform).toBe("translate(100px, 50px)");
});

test("should respect minimum drag bounds", () => {
  // Add test for minimum drag boundaries
});
apps/docs/content/docs/components/drawer.mdx (3)

31-60: Add context about import strategies.

Consider adding a brief explanation about when to use the main @nextui-org/react import versus individual @nextui-org/drawer imports. This would help developers make informed decisions about bundle size optimization.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~49-~49: Loose punctuation mark.
Context: ...rawerFooter } from "@nextui-org/react";, individual: import { Drawe...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~57-~57: Loose punctuation mark.
Context: ...awerFooter } from "@nextui-org/drawer";`, }} /> ## Usage When the drawer open...

(UNLIKELY_OPENING_PUNCTUATION)


70-72: Add default size information.

Consider adding the default size value in the sizes section introduction to help developers make informed decisions about customization.


153-170: Enhance props documentation.

Consider the following improvements to the props table:

  1. Add type information for the backdrop prop that was mentioned in the usage section
  2. Consider grouping related props (e.g., grouping all dismissal-related props together)
  3. Add examples for the classNames prop usage
🧰 Tools
🪛 LanguageTool

[uncategorized] ~165-~165: Consider adding a hyphen.
Context: ... | Custom close button to display on top right corner. ...

(TOP_LEFT_CORNER)

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

Line range hint 1-262: Standardize component status flags

There's an inconsistency in how new components are marked:

  • Most new components use "newPost": true
  • The Alert component uses "isNew": true

Consider standardizing all status flags to use newPost for consistency.

Example for Alert component:

{
  "key": "alert",
  "title": "Alert",
  "keywords": "alert, notification, message",
  "path": "/docs/components/alert.mdx",
-  "isNew": true
+  "newPost": true
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a4a1d8f and 884fc4c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • apps/docs/config/routes.json (1 hunks)
  • apps/docs/content/components/drawer/backdrop.ts (1 hunks)
  • apps/docs/content/components/drawer/custom-motion.ts (1 hunks)
  • apps/docs/content/components/drawer/form.ts (1 hunks)
  • apps/docs/content/components/drawer/index.ts (1 hunks)
  • apps/docs/content/components/drawer/non-dismissable.ts (1 hunks)
  • apps/docs/content/components/drawer/placement.ts (1 hunks)
  • apps/docs/content/components/drawer/sizes.ts (1 hunks)
  • apps/docs/content/components/drawer/usage.ts (1 hunks)
  • apps/docs/content/components/index.ts (1 hunks)
  • apps/docs/content/docs/components/drawer.mdx (1 hunks)
  • packages/components/drawer/README.md (1 hunks)
  • packages/components/drawer/__tests__/drawer.test.tsx (1 hunks)
  • packages/components/drawer/package.json (1 hunks)
  • packages/components/drawer/src/drawer.tsx (1 hunks)
  • packages/components/drawer/src/index.ts (1 hunks)
  • packages/components/drawer/src/use-drawer.ts (1 hunks)
  • packages/components/drawer/stories/drawer.stories.tsx (1 hunks)
  • packages/components/drawer/tsconfig.json (1 hunks)
  • packages/components/drawer/tsup.config.ts (1 hunks)
  • packages/components/modal/__tests__/modal.test.tsx (1 hunks)
  • packages/core/react/package.json (1 hunks)
  • packages/core/react/src/index.ts (1 hunks)
  • packages/core/theme/src/components/alert.ts (6 hunks)
  • packages/core/theme/src/components/drawer.ts (1 hunks)
  • packages/core/theme/src/components/index.ts (1 hunks)
  • packages/core/theme/src/utils/variants.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/components/drawer/tsconfig.json
  • packages/components/drawer/tsup.config.ts
🧰 Additional context used
🪛 LanguageTool
apps/docs/content/docs/components/drawer.mdx

[uncategorized] ~49-~49: Loose punctuation mark.
Context: ...rawerFooter } from "@nextui-org/react";, individual: import { Drawe...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~57-~57: Loose punctuation mark.
Context: ...awerFooter } from "@nextui-org/drawer";`, }} /> ## Usage When the drawer open...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~165-~165: Consider adding a hyphen.
Context: ... | Custom close button to display on top right corner. ...

(TOP_LEFT_CORNER)

packages/components/drawer/README.md

[style] ~3-~3: Consider using the synonym “brief” (= concise, using a few words, not lasting long) to strengthen your wording.
Context: # @nextui-org/drawer A Quick description of the component > This is...

(QUICK_BRIEF)


[typographical] ~17-~17: Usually, a comma is necessary before ‘Yes’ at the end of a sentence. Did you mean “, please”?
Context: ...tui-org/drawer ``` ## Contribution Yes please! See the [contributing guidelines](http...

(COMMA_BEFORE_PLEASE)

🔇 Additional comments (31)
packages/components/drawer/src/index.ts (3)

5-6: Well-organized exports with clear separation of concerns.

The exports are well-structured, following a clear pattern:

  • Types are exported separately
  • Hooks are isolated
  • Main component is exported independently

This organization makes it easier for users to import only what they need, potentially enabling better tree-shaking.

Also applies to: 8-12


14-20: Consider documenting the relationship between Modal and Drawer components.

The re-export of Modal subcomponents as Drawer components is a good approach for code reuse. However, it would be beneficial to:

  1. Document this relationship in the component's documentation
  2. Ensure that all Modal props relevant to Drawer functionality are properly typed in the DrawerProps interface

1-3: Verify Modal dependency compatibility.

The Drawer component is built on top of the Modal component, which is a common and efficient pattern. However, we should ensure that the Modal component supports all the features needed for a Drawer implementation.

✅ Verification successful

Modal dependency is appropriately used and compatible with Drawer requirements

The implementation shows that:

  • The Drawer component extends Modal functionality while properly handling drawer-specific requirements
  • It implements custom placement logic ("top", "right", "bottom", "left") with appropriate animations
  • Modal components (Header, Body, Footer, Content) are correctly imported and used as building blocks
  • The implementation properly omits Modal-specific props that aren't relevant for Drawer (placement, scrollBehavior)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Modal component capabilities and props
# Expected: Modal component should support placement/position props needed for drawer functionality

# Check Modal component implementation
ast-grep --pattern 'interface ModalProps {
  $$$
}'

# Check Modal usage in tests
rg -A 5 "Modal.*placement|position" packages/components/modal/__tests__/

Length of output: 176


Script:

#!/bin/bash
# Let's check the actual implementation and props of both Modal and Drawer

# Check Modal props and implementation
ast-grep --pattern 'interface ModalProps {
  $$$
}'

# Check Drawer props
ast-grep --pattern 'interface DrawerProps {
  $$$
}'

# Check how Drawer uses Modal components
rg -A 10 "import.*Modal" packages/components/drawer/src/

Length of output: 2601


Script:

#!/bin/bash
# Let's check the placement implementation details

# Check placement prop definition
rg -A 5 "placement.*:" packages/components/drawer/src/

# Check how placement is handled in the implementation
ast-grep --pattern 'useDrawer($$$) {
  $$$
}'

# Check if there are any drawer-specific styles or animations
rg -A 5 "drawer.*={" packages/components/drawer/src/

Length of output: 1574

packages/components/drawer/src/drawer.tsx (2)

10-18: Add JSDoc documentation and improve type safety.

The component would benefit from proper documentation and explicit type annotations.

+/**
+ * A drawer component that slides in from the edge of the screen.
+ * Built on top of Modal component with additional positioning and animation features.
+ *
+ * @example
+ * ```tsx
+ * <Drawer placement="left" isOpen onClose={() => setOpen(false)}>
+ *   <DrawerContent>Drawer content</DrawerContent>
+ * </Drawer>
+ * ```
+ */
-const Drawer = forwardRef<"div", DrawerProps>(({children, ...props}, ref) => {
+const Drawer = forwardRef<HTMLDivElement, DrawerProps>(({children, ...props}, ref) => {

Let's verify the accessibility features inherited from Modal:

#!/bin/bash
# Description: Check Modal component's accessibility implementation
# Expected: Find ARIA attributes and keyboard handling

# Search for ARIA attributes and role definitions in Modal
rg -A 5 'aria-|role=' packages/components/modal/

# Search for keyboard event handlers
rg -A 5 'onKey|handleKey' packages/components/modal/

20-22: Verify component exports in the public API.

The component's export setup looks good, but let's ensure it's properly exposed in the public API.

✅ Verification successful

Let me generate additional scripts to verify the package.json configuration since the previous command didn't show the package.json fields correctly.


Let me try one more verification to check the core package exports with a broader search:


Component is properly exported in the public API

Based on the verification results:

  • The Drawer component is correctly exported in its package's index file
  • The component is properly re-exported in the main NextUI package via @nextui-org/drawer
  • The package.json configuration is set up correctly with the main entry point
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Drawer component exports
# Expected: Find Drawer exports in index files and package.json

# Check if Drawer is exported in the package's index file
rg -A 5 "export.*Drawer" packages/components/drawer/

# Verify package.json main/module/types fields
fd -e json -x jq -r '.main, .module, .types' {} \; packages/components/drawer/package.json

Length of output: 2052


Script:

#!/bin/bash
# Check package.json content directly
cat packages/components/drawer/package.json

# Also verify if the component is re-exported in the main nextui package
rg -A 3 "drawer" packages/core/nextui/src/index.ts

Length of output: 2004


Script:

#!/bin/bash
# Find the main NextUI package index file
fd -t f index.ts -x cat {} \; packages/core/

# Also check if there are any other index files that might re-export the drawer
fd -t f index.ts packages/

Length of output: 11348

apps/docs/content/components/index.ts (1)

35-35: LGTM! The export statement follows the established pattern.

The addition of the drawer export maintains consistency with other component exports and is necessary for making the drawer component available through the public API.

Let's verify that the drawer component exists:

✅ Verification successful

Export statement is correctly implemented and the drawer component is properly structured

The verification confirms that:

  • The drawer component exists in the packages directory
  • The drawer documentation is properly structured in apps/docs/content/components/drawer/
  • The export follows the established pattern and points to an existing module
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the drawer component
# Expected: Find the drawer component implementation

# Test 1: Check if the drawer component exists
fd -t f "drawer.tsx" -X echo "Found drawer component at: {}"

# Test 2: Verify the component's export structure
ast-grep --pattern 'export * from "./drawer"'

Length of output: 319


Script:

#!/bin/bash
# Let's verify the drawer component's implementation and exports
# Check if there's a corresponding drawer.ts file in the docs content directory
fd -t f "drawer.ts" -d 1 apps/docs/content/components

# Check the content of the drawer export file in docs
rg -g "drawer.ts" "export" apps/docs/content/components

Length of output: 107


Script:

#!/bin/bash
# Let's check the drawer directory structure and content
fd -t f "drawer" apps/docs/content/components

# Check if there's a drawer directory with index file
fd -t f "index.ts" apps/docs/content/components/drawer

# Look for any drawer-related exports in the components directory
rg -g "*.ts" "drawer" apps/docs/content/components

Length of output: 587

packages/core/theme/src/components/index.ts (2)

42-42: LGTM! Export follows consistent pattern

The new drawer export follows the established pattern and is correctly placed in the theme components index.


42-42: Verify drawer theme file existence

Let's ensure the corresponding drawer theme file exists and is properly structured.

✅ Verification successful

✓ Drawer theme file exists and is properly structured

The drawer theme file exists at the correct location and follows the expected structure with:

  • Proper theme configuration using tv utility
  • Well-defined variants for size and placement
  • Correct exports of both the drawer configuration and its type
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the drawer theme file exists and follows the expected structure

# Test 1: Check if the drawer theme file exists
fd -t f "drawer.ts" "packages/core/theme/src/components"

# Test 2: Verify the theme structure
if [ -f "packages/core/theme/src/components/drawer.ts" ]; then
    # Look for theme export pattern
    rg -l "export const drawer\s*=\s*{" "packages/core/theme/src/components/drawer.ts"
fi

Length of output: 358


Script:

#!/bin/bash
# Let's verify the drawer theme file content with correct regex
# First, check if file exists and show its content
cat packages/core/theme/src/components/drawer.ts

# Also check for any export pattern without regex
rg "export" packages/core/theme/src/components/drawer.ts

Length of output: 1736

apps/docs/content/components/drawer/non-dismissable.ts (1)

46-52: LGTM! Export configuration is well-structured.

The export structure is clean and allows for easy integration with the documentation system.

packages/core/react/src/index.ts (1)

47-47: LGTM! The export follows the established patterns.

The new drawer export is consistent with the module naming convention and is properly grouped with other UI component exports.

Let's verify the drawer package is properly listed as a dependency:

✅ Verification successful

Drawer package is properly configured as a workspace dependency

The verification confirms:

  • The drawer package is correctly listed as a workspace dependency in packages/core/react/package.json
  • The package exists in the expected location at packages/components/drawer/
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify drawer package dependency
# Expected: The drawer package should be listed in package.json dependencies

# Test 1: Check if drawer is listed in dependencies
jq -r '.dependencies["@nextui-org/drawer"]' packages/core/react/package.json

# Test 2: Verify the package exists in the workspace
fd -t d "drawer$" packages/components/

Length of output: 159

apps/docs/content/components/drawer/custom-motion.ts (1)

1-5: LGTM: Clean component setup with proper imports and hooks usage

The component structure follows React best practices with appropriate imports and proper state management using useDisclosure.

packages/components/drawer/package.json (3)

13-26: Package configuration looks good

The package configuration follows NextUI's standard patterns with correct repository links and distribution settings.


27-35: Build and development scripts are properly configured

The scripts follow NextUI's standard component setup with appropriate build, development, and cleaning tasks.

Also applies to: 63-63


36-62: Verify architectural dependency on @nextui-org/modal

The drawer component depends on @nextui-org/modal. While this is a common pattern, ensure this dependency is necessary and doesn't create potential circular dependencies or tight coupling.

✅ Verification successful

Dependency on @nextui-org/modal is well-justified and properly implemented

The drawer component is intentionally built on top of the Modal component, using it as a base while adding drawer-specific functionality:

  • It extends Modal's props and adds drawer-specific properties (placement, scrollBehavior)
  • Uses Modal as a wrapper component with custom motion and styling props
  • No circular dependencies were found
  • The implementation follows the composition pattern, which is a good architectural choice
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential circular dependencies between modal and drawer
echo "Checking for potential circular dependencies..."
rg -l "@nextui-org/drawer" packages/components/modal/

Length of output: 158


Script:

#!/bin/bash
# Let's analyze the drawer implementation to understand its relationship with modal
echo "Checking drawer's usage of modal..."
fd -e tsx -e ts drawer.tsx packages/components/drawer/ --exec ast-grep --pattern 'import {$$$} from "@nextui-org/modal"'

echo -e "\nChecking drawer's implementation..."
fd -e tsx -e ts drawer.tsx packages/components/drawer/ --exec cat

Length of output: 982


Script:

#!/bin/bash
# Let's check the use-drawer hook to understand how it uses modal functionality
echo "Checking use-drawer hook implementation..."
fd -e tsx -e ts use-drawer.tsx? packages/components/drawer/ --exec cat

# Let's also check if there are any other components that might be affected
echo -e "\nChecking for other components using drawer..."
rg -l "@nextui-org/drawer" packages/components/ --type ts --type tsx

Length of output: 2784

apps/docs/content/components/drawer/usage.ts (2)

1-5: LGTM! Clean setup with proper state management.

The component setup follows React best practices, using the useDisclosure hook for managing drawer state. The template string approach is appropriate for documentation purposes.


49-55: LGTM! Clean export configuration.

The export structure is appropriate for documentation purposes.

apps/docs/content/components/drawer/placement.ts (2)

7-10: LGTM! Clean and efficient event handler implementation

The handler correctly manages both the placement state and drawer visibility.


62-68: LGTM! Export configuration is correct

The export structure follows the documentation pattern and is properly implemented.

packages/core/theme/src/utils/variants.ts (1)

33-37: Review color contrast and accessibility implications.

The changes to the flat variant's text colors show a systematic lightening of shades, which could affect readability:

  1. The shift from 700/800 to 600/700 makes text colors consistently lighter
  2. Dark mode colors are explicitly defined for success/warning/danger states

Consider verifying that these color combinations meet WCAG contrast requirements, especially for the flat variant which now uses lighter text colors (600/700) on light backgrounds (20% opacity).

✅ Verification successful

Let me gather more specific information about color contrast testing in the codebase.


Based on the gathered information, I can now provide a definitive response about the color contrast concerns.

Color contrast changes have been verified and are part of an accessibility improvement

The changes to the flat variant's text colors were intentionally made to improve accessibility, as evidenced by:

  1. The codebase has built-in color contrast testing (packages/core/theme/tests/semantic-colors.test.ts) that enforces minimum contrast ratios:

    • "decorative": 1.5:1
    • "readable": 3:1 (default target)
    • "aa": 4.5:1
    • "aaa": 7:1
  2. The changes are documented in the changelog (packages/core/theme/CHANGELOG.md) as an intentional accessibility improvement:

    Updates the text colors for the flat variant in the theme so that elements using this variant are accessible (fix(theme): update flat variant text colors to be accessible #3738)

The systematic lightening of shades and addition of dark mode colors was a deliberate enhancement to meet accessibility standards, not a potential issue.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any color contrast testing configurations or utilities in the codebase
rg -g '!node_modules' -g '!dist' -i 'contrast|wcag|a11y|accessibility'

# Look for any existing color token definitions that might be affected
fd -e ts -e js -e json . | xargs rg -l 'text-(primary|secondary|success|warning|danger)'

Length of output: 28717


Script:

#!/bin/bash
# Check color contrast testing implementation
cat packages/core/theme/__tests__/semantic-colors.test.ts

# Check the flat variant implementation in other components
rg -g '!node_modules' -g '!dist' 'flat.*variant' -A 5 -B 5

Length of output: 48307

packages/core/react/package.json (1)

88-88: LGTM! The drawer dependency is properly integrated.

The addition of @nextui-org/drawer follows the established pattern for component dependencies in the NextUI React core package.

Let's verify the drawer package setup:

✅ Verification successful

The drawer package is properly set up and integrated

The verification confirms that:

  • The drawer package is correctly structured with proper package.json configuration
  • All required peer dependencies are declared (@nextui-org/theme, @nextui-org/system)
  • The component is properly implemented using the Modal component as base
  • The package is correctly exported in the main NextUI React package
  • Version 2.0.0 matches the PR context
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the drawer package configuration
# Test 1: Check if drawer package.json exists and has required fields
fd -t f "package.json" -p "packages/components/drawer" --exec cat {} \
  | jq 'select(.name=="@nextui-org/drawer") | {name, version, peerDependencies, main, types, module}'

# Test 2: Verify exports in the main package
rg -A 5 "export \* from .*drawer" packages/core/react/src/index.ts

Length of output: 678


Script:

#!/bin/bash
# Let's fix the JSON parsing and try again with proper quoting
fd -t f "package.json" -p "packages/components/drawer" --exec cat {} \
  | jq '{name, version, peerDependencies, main, types, module}'

# Also check if the drawer component exists and is properly structured
fd -t f -e ts -e tsx . -p "packages/components/drawer/src" --exec ls -la {}

Length of output: 716


Script:

#!/bin/bash
# Let's check the package.json content directly without jq
cat packages/components/drawer/package.json

# And examine the drawer component implementation
cat packages/components/drawer/src/drawer.tsx
cat packages/components/drawer/src/use-drawer.ts

Length of output: 4922

apps/docs/content/components/drawer/backdrop.ts (1)

124-132: LGTM!

The export configuration is properly structured for documentation purposes.

packages/core/theme/src/components/alert.ts (4)

4-4: LGTM: Good use of shared color utilities

The addition of colorVariants import promotes code reuse and consistency across components.


38-43: LGTM: Good refactoring of color variants

Empty color variant objects are appropriate here since the actual color classes are now managed by the colorVariants utility. This reduces duplication and makes color management more maintainable.


74-75: LGTM: Consistent with string-based approach

The hasDescription variant changes align well with the overall move to string-based class definitions.


Line range hint 90-229: Verify color consistency across variants

The refactoring to use colorVariants looks good and improves maintainability. However, let's verify that all color variants maintain consistent behavior.

✅ Verification successful

Color variants implementation is consistent and well-structured

The verification shows that colorVariants is consistently used across multiple components (alert, button, badge, etc.) and follows a unified pattern. The implementation in variants.ts exports a structured object with all necessary variant types (solid, shadow, bordered, flat, faded, light, ghost), which are consistently applied in the alert component's modifications.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify color consistency across components using colorVariants

# Test: Check if colorVariants is consistently used across other components
rg -l "colorVariants\." packages/core/theme/src/components/

Length of output: 509


Script:

#!/bin/bash
# Check the color variants implementation in one of the components to verify consistency
rg -A 5 "colorVariants\." packages/core/theme/src/components/button.ts

# Also check the source of colorVariants
fd colorVariants.ts packages/core/theme/src/

Length of output: 5263


Script:

#!/bin/bash
# Let's find and check the colorVariants implementation
rg -p "export const colorVariants" packages/core/theme/src/ -A 10

Length of output: 236

packages/components/modal/__tests__/modal.test.tsx (1)

Line range hint 142-191: Consider viewport scaling in drag calculations

The current tests use fixed pixel values which might not work correctly with different DPI settings. Consider using relative units or accounting for window.devicePixelRatio in the calculations.

apps/docs/content/docs/components/drawer.mdx (1)

1-12: LGTM! Clear and concise component introduction.

The component's metadata and description effectively communicate its purpose and functionality.

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

256-262: LGTM: Drawer component route properly configured

The new Drawer component route is well-structured with appropriate keywords and follows the established documentation patterns.

packages/components/drawer/src/use-drawer.ts (3)

1-9: Import statements are appropriate and necessary

The imported modules and types are correctly used throughout the file.


10-23: Props interface is well-defined and extends ModalProps correctly

The interface accurately defines the required properties for the drawer component, omitting specific modal props as intended.


27-97: useDrawer hook is implemented correctly and follows React best practices

The hook effectively manages the drawer's state and properties using useMemo and useCallback, ensuring optimal performance.

Comment on lines +1 to +8
import {forwardRef} from "@nextui-org/system";
import {Modal} from "@nextui-org/modal";

import {useDrawer, UseDrawerProps} from "./use-drawer";

export interface DrawerProps extends UseDrawerProps {
children: React.ReactNode;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing React import.

The code uses React.ReactNode type but React is not imported.

+import {type ReactNode} from "react";
 import {forwardRef} from "@nextui-org/system";
 import {Modal} from "@nextui-org/modal";

 import {useDrawer, UseDrawerProps} from "./use-drawer";

 export interface DrawerProps extends UseDrawerProps {
-  children: React.ReactNode;
+  children: ReactNode;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import {forwardRef} from "@nextui-org/system";
import {Modal} from "@nextui-org/modal";
import {useDrawer, UseDrawerProps} from "./use-drawer";
export interface DrawerProps extends UseDrawerProps {
children: React.ReactNode;
}
import {type ReactNode} from "react";
import {forwardRef} from "@nextui-org/system";
import {Modal} from "@nextui-org/modal";
import {useDrawer, UseDrawerProps} from "./use-drawer";
export interface DrawerProps extends UseDrawerProps {
children: ReactNode;
}

Comment on lines +9 to +14
<Drawer
isOpen={isOpen}
onOpenChange={onOpenChange}
isDismissable={false}
isKeyboardDismissDisabled={true}
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add accessibility considerations for non-dismissable drawers.

While the non-dismissable configuration serves its purpose, it's important to note the accessibility implications. Consider adding an ARIA label and role to improve screen reader support, and document when to use this pattern.

       <Drawer 
         isOpen={isOpen} 
         onOpenChange={onOpenChange}
         isDismissable={false}
         isKeyboardDismissDisabled={true}
+        aria-label="Non-dismissable drawer"
+        role="dialog"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Drawer
isOpen={isOpen}
onOpenChange={onOpenChange}
isDismissable={false}
isKeyboardDismissDisabled={true}
>
<Drawer
isOpen={isOpen}
onOpenChange={onOpenChange}
isDismissable={false}
isKeyboardDismissDisabled={true}
aria-label="Non-dismissable drawer"
role="dialog"
>

Comment on lines +20 to +23
const drawer = tv({
slots: {
base: ["absolute", "m-0", "sm:m-0", "overflow-y-auto"],
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add essential base styles for proper drawer functionality

The base styles are missing some crucial properties:

  1. z-index for proper stacking order
  2. Background color for visual separation
  3. Box-shadow for depth perception
 const drawer = tv({
   slots: {
-    base: ["absolute", "m-0", "sm:m-0", "overflow-y-auto"],
+    base: [
+      "absolute",
+      "m-0",
+      "sm:m-0",
+      "overflow-y-auto",
+      "z-50",
+      "bg-background",
+      "shadow-lg"
+    ],
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const drawer = tv({
slots: {
base: ["absolute", "m-0", "sm:m-0", "overflow-y-auto"],
},
const drawer = tv({
slots: {
base: [
"absolute",
"m-0",
"sm:m-0",
"overflow-y-auto",
"z-50",
"bg-background",
"shadow-lg"
],
},

Comment on lines +1 to +76
import {VariantProps} from "tailwind-variants";

import {tv} from "../utils/tv";

/**
* Modal **Tailwind Variants** component
*
* @example
* ```js
* const {base} = drawer({...})
*
* <div>
* <button>Open Drawer</button>
* <div className={base()}>
* Drawer Content
* </div>
* </div>
* ```
*/
const drawer = tv({
slots: {
base: ["absolute", "m-0", "sm:m-0", "overflow-y-auto"],
},
variants: {
size: {
xs: {
base: "max-w-xs max-h-[20rem]",
},
sm: {
base: "max-w-sm max-h-[24rem]",
},
md: {
base: "max-w-md max-h-[28rem]",
},
lg: {
base: "max-w-lg max-h-[32rem]",
},
xl: {
base: "max-w-xl max-h-[36rem]",
},
"2xl": {
base: "max-w-2xl max-h-[42rem]",
},
"3xl": {
base: "max-w-3xl max-h-[48rem]",
},
"4xl": {
base: "max-w-4xl max-h-[56rem]",
},
"5xl": {
base: "max-w-5xl max-h-[64rem]",
},
full: {
base: "max-w-full max-h-full h-[100dvh] !rounded-none",
},
},
placement: {
top: {
base: "inset-x-0 top-0 max-w-[none] rounded-t-none",
},
right: {
base: "inset-y-0 right-0 max-h-[none] rounded-r-none",
},
bottom: {
base: "inset-x-0 bottom-0 max-w-[none] rounded-b-none",
},
left: {
base: "inset-y-0 left-0 max-h-[none] rounded-l-none",
},
},
},
});

export type DrawerVariants = VariantProps<typeof drawer>;

export {drawer};
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Theme integration needs to be enhanced

The drawer component requires updates to align with NextUI's theming system:

  • Add semantic color tokens (e.g., bg-content1, text-foreground-500) instead of basic colors like bg-white
  • Include backdrop blur and overlay styles similar to the modal component
  • Add hover and active states with proper color tokens

Example locations that need attention:

  • base slot should use semantic background color token
  • Add proper color tokens for text and borders if needed
  • Consider adding backdrop styles similar to modal's bg-overlay/30 with backdrop-blur-md
🔗 Analysis chain

Verify theme integration with NextUI ecosystem

Please ensure that:

  1. The drawer component respects the NextUI theme tokens
  2. The styling approach is consistent with other NextUI components
  3. The component works correctly with NextUI's dark mode

Let me gather more specific information about the theme implementation patterns in NextUI components.


Let me fix the regex and try again to gather the necessary information.


Let me check one more aspect regarding the theme system implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check theme token usage and styling consistency
rg -l "theme|darkMode" packages/core/theme/src/components/

Length of output: 430


Script:

#!/bin/bash
# Check theme implementation patterns in similar components
rg -A 5 "tv\({" packages/core/theme/src/components/popover.ts packages/core/theme/src/components/modal.ts

# Look for dark mode handling patterns
ast-grep --pattern 'darkMode: {
  $$$
}'

Length of output: 224


Script:

#!/bin/bash
# Check theme implementation patterns in similar components
rg "const \w+ = tv\(" -A 10 packages/core/theme/src/components/popover.ts packages/core/theme/src/components/modal.ts

# Check color and theme token usage
rg "colors|tokens" packages/core/theme/src/components/popover.ts packages/core/theme/src/components/modal.ts

# Check base styles implementation
rg "base: \[" -A 5 packages/core/theme/src/components/popover.ts packages/core/theme/src/components/modal.ts

Length of output: 2488


Script:

#!/bin/bash
# Check theme system implementation and dark mode handling
rg "bg-|text-|border-" packages/core/theme/src/components/modal.ts packages/core/theme/src/components/drawer.ts

# Check theme configuration
cat packages/core/theme/src/theme-provider.tsx

Length of output: 921

Comment on lines +16 to +20
<div className="flex flex-wrap gap-3">
{sizes.map((size) => (
<Button key={size} onPress={() => handleOpen(size)}>Open {size}</Button>
))}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance button accessibility

The buttons should have proper ARIA labels to improve accessibility for screen readers.

-          <Button key={size} onPress={() => handleOpen(size)}>Open {size}</Button>
+          <Button 
+            key={size} 
+            onPress={() => handleOpen(size)}
+            aria-label={`Open ${size} size drawer`}
+          >
+            Open {size}
+          </Button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="flex flex-wrap gap-3">
{sizes.map((size) => (
<Button key={size} onPress={() => handleOpen(size)}>Open {size}</Button>
))}
</div>
<div className="flex flex-wrap gap-3">
{sizes.map((size) => (
<Button
key={size}
onPress={() => handleOpen(size)}
aria-label={`Open ${size} size drawer`}
>
Open {size}
</Button>
))}
</div>

Comment on lines +45 to +54
export default function App() {
const {isOpen, onOpen, onOpenChange} = useDisclosure();
const [backdrop, setBackdrop] = React.useState("opaque");

const backdrops = ["opaque", "blur", "transparent"];

const handleBackdropChange = (backdrop) => {
setBackdrop(backdrop);
onOpen();
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding state management for form data and loading state.

The current implementation lacks form state management and loading states for async operations.

Example implementation:

const [formData, setFormData] = React.useState({ email: '', password: '' });
const [isLoading, setIsLoading] = React.useState(false);

const handleSignIn = async () => {
  setIsLoading(true);
  try {
    // Handle sign in
  } catch (error) {
    // Handle error
  } finally {
    setIsLoading(false);
  }
};

Comment on lines +77 to +94
<Input
autoFocus
endContent={
<MailIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
}
label="Email"
placeholder="Enter your email"
variant="bordered"
/>
<Input
endContent={
<LockIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
}
label="Password"
placeholder="Enter your password"
type="password"
variant="bordered"
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance form inputs with validation and error handling.

The email and password inputs should include:

  1. Form validation
  2. Error messages
  3. Proper aria-labels

Example improvement:

<Input
  autoFocus
  required
  type="email"
  label="Email"
  placeholder="Enter your email"
  variant="bordered"
  errorMessage={errors.email}
  aria-label="Email input"
  onChange={(e) => setFormData({ ...formData, email: e.target.value })}
  endContent={<MailIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />}
/>

Comment on lines +108 to +115
<DrawerFooter>
<Button color="danger" variant="flat" onPress={onClose}>
Close
</Button>
<Button color="primary" onPress={onClose}>
Sign in
</Button>
</DrawerFooter>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Separate close and sign-in actions with proper loading state.

The current implementation uses the same onClose callback for both actions without proper loading state handling.

Example improvement:

<DrawerFooter>
  <Button color="danger" variant="flat" onPress={onClose}>
    Close
  </Button>
  <Button 
    color="primary" 
    isLoading={isLoading}
    onPress={handleSignIn}
  >
    Sign in
  </Button>
</DrawerFooter>

Comment on lines +73 to +121
const content = (
<DrawerContent>
{(onClose) => (
<>
<DrawerHeader className="flex flex-col gap-1">Log in</DrawerHeader>
<DrawerBody>
<Input
autoFocus
endContent={
<MailFilledIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
}
label="Email"
placeholder="Enter your email"
variant="bordered"
/>
<Input
endContent={
<LockFilledIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
}
label="Password"
placeholder="Enter your password"
type="password"
variant="bordered"
/>
<div className="flex py-2 px-1 justify-between">
<Checkbox
classNames={{
label: "text-sm",
}}
>
Remember me
</Checkbox>
<Link color="primary" href="#" size="sm">
Forgot password?
</Link>
</div>
</DrawerBody>
<DrawerFooter>
<Button color="danger" variant="flat" onPress={onClose}>
Close
</Button>
<Button color="primary" onPress={onClose}>
Sign in
</Button>
</DrawerFooter>
</>
)}
</DrawerContent>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility and form handling.

The login form implementation could be improved in several ways:

  1. Add aria-label to the DrawerContent for screen readers
  2. Wrap the inputs in a <form> element for proper form submission
  3. Add form validation and error handling
 const content = (
-  <DrawerContent>
+  <DrawerContent aria-label="Login form">
     {(onClose) => (
       <>
         <DrawerHeader className="flex flex-col gap-1">Log in</DrawerHeader>
         <DrawerBody>
+          <form onSubmit={(e) => {
+            e.preventDefault();
+            // Handle form submission
+          }}>
           <Input
             autoFocus
             endContent={
               <MailFilledIcon className="text-2xl text-default-400 pointer-events-none flex-shrink-0" />
             }
             label="Email"
             placeholder="Enter your email"
             variant="bordered"
+            required
+            type="email"
           />
           {/* ... rest of the form ... */}
+          </form>
         </DrawerBody>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +86 to +90
The drawer can be placed on any edge of the screen using the `placement` prop:
- `left` (default)
- `right`
- `top`
- `bottom`
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix inconsistent default placement value.

There's a discrepancy in the documentation regarding the default placement:

  • Line 87 states left is the default
  • The props table on line 158 shows right as the default

Please verify and update to show the correct default value consistently throughout the documentation.

Also applies to: 158-158

@jrgarciadev jrgarciadev merged commit 58a77cb into canary Nov 4, 2024
8 checks passed
@jrgarciadev jrgarciadev deleted the feat/drawer branch November 4, 2024 20:20
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.

[Feature Request] Need A Drawer Component [Feature Request] Drawer Component
2 participants