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

[WEB-4205] - Flyout Component for Meganav #625

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

aralovelace
Copy link
Contributor

@aralovelace aralovelace commented Jan 31, 2025

Jira Ticket Link / Motivation

WEB-4205 - Flyout component for Meganav based on the design requirements

Summary of changes

This pull request introduces a new navigation menu component using the @radix-ui/react-navigation-menu package and updates related files to integrate and test this new component.

New Navigation Menu Component:

  • package.json: Added @radix-ui/react-navigation-menu dependency to the project.
  • src/core/Flyout.tsx: Created a new NavigationMenuDemo component using @radix-ui/react-navigation-menu to provide a flexible and styled navigation menu.

Storybook Integration:

Tailwind Config Update:

Added a new animation scaleIn and scaleOut to be used in the flyout component upon hovering the menu link.

Snapshot Testing:

How do you manually test this?

Reviewer Tasks (optional)

Merge/Deploy Checklist

  • Written automated tests for implemented features/fixed bugs
  • Rebased and squashed commits
  • Commits have clear descriptions of their changes
  • Checked for any performance regressions

Frontend Checklist

  • No frontend changes in this PR
  • Added before/after screenshots for changes
  • Tested on different platforms/browsers with Browserstack
  • Compared with the initial design / our brand guidelines
  • Checked the code for accessibility issues (VoiceOver User Guide)?

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added a navigation menu component with dynamic menu items and customizable styling.
    • Introduced new scale-based animations for UI interactions.
  • Dependencies

    • Added @radix-ui/react-navigation-menu package for enhanced navigation capabilities.
  • Storybook

    • Created a new Storybook story for the Flyout component with example configurations, including various display options.

@aralovelace aralovelace self-assigned this Jan 31, 2025
Copy link
Contributor

coderabbitai bot commented Jan 31, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces a new navigation menu component using Radix UI's React Navigation Menu library. The changes include adding a new dependency to package.json, creating a Flyout component in Flyout.tsx, developing a Storybook configuration for the component, and extending Tailwind CSS configuration with new keyframe animations. The implementation provides a flexible navigation menu system with customizable menu items, content, and styling.

Changes

File Change Summary
package.json Added @radix-ui/react-navigation-menu dependency
src/core/Flyout.tsx Created Flyout component with dynamic menu rendering and FlyOverlay component
src/core/Flyout/Flyout.stories.tsx Added Storybook configuration with menu items and stories for Flyout
tailwind.config.js Introduced new keyframe animations: scale-in, scale-out, enter-from-right, enter-from-left, exit-to-right, exit-to-left

Possibly related PRs

Suggested labels

review

Suggested reviewers

  • jamiehenson
  • kennethkalmer

Poem

🐰 Hop into the menu, with Radix so fine,
Scaling animations that smoothly align,
Components that dance with elegance and grace,
A navigation symphony in digital space!
Radix UI magic, our code now shines bright! 🚀


🪧 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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

@aralovelace aralovelace force-pushed the WEB-4205_flyout branch 2 times, most recently from 36e72ad to 6b7cb89 Compare January 31, 2025 09:04
@aralovelace
Copy link
Contributor Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jan 31, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (3)
src/core/Flyout.tsx (2)

13-26: Consider enhancing type safety with proper TypeScript interfaces.

The component's props would benefit from being defined as a separate interface for better maintainability and documentation.

+interface MenuItem {
+  label: string;
+  content?: React.ReactNode;
+  link?: string;
+  panelStyling?: string;
+}
+
+interface NavigationMenuDemoProps {
+  menuItems: MenuItem[];
+  navMenuStyling?: string;
+  flyOutStyling?: string;
+}
+
-const NavigationMenuDemo = ({
+const NavigationMenuDemo = ({
   menuItems,
   navMenuStyling,
   flyOutStyling,
-}: {
-  menuItems: {
-    label: string;
-    content?: React.ReactNode;
-    link?: string;
-    panelStyling?: string;
-  }[];
-  navMenuStyling?: string;
-  flyOutStyling?: string;
-}) => {
+}: NavigationMenuDemoProps) => {

59-63: Add aria-label to improve accessibility.

The navigation link should have an aria-label for better screen reader support.

-              <NavigationMenuLink>
+              <NavigationMenuLink aria-label={menuItem.label}>
                 <a href={menuItem.link} className={menuLinkStyles}>
                   {menuItem.label}
                 </a>
               </NavigationMenuLink>
src/core/Flyout/Flyout.stories.tsx (1)

16-23: Consider moving sample data to a separate file.

The product and platform arrays should be moved to a separate constants file for better maintainability and reusability.

+// src/core/Flyout/constants.ts
+export const SAMPLE_PRODUCTS: ProductName[] = [
+  "pubsub",
+  "chat",
+  "spaces",
+  "liveSync",
+  "assetTracking",
+  "liveObjects",
+];
+
+export const SAMPLE_PLATFORMS = [
+  "Infrastructure",
+  "Integrations",
+  "SDKs",
+  "Security & Compliance",
+];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3445311 and 6b7cb89.

⛔ Files ignored due to path filters (2)
  • src/core/Flyout/__snapshots__/Flyout.stories.tsx.snap is excluded by !**/*.snap
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • src/core/Flyout.tsx (1 hunks)
  • src/core/Flyout/Flyout.stories.tsx (1 hunks)
  • tailwind.config.js (1 hunks)
🔇 Additional comments (2)
tailwind.config.js (1)

335-342: LGTM! Well-implemented animations.

The new scale animations are well-defined and follow Tailwind's animation conventions. The timing and easing functions are appropriate for a navigation menu.

Also applies to: 347-348

package.json (1)

84-84: Verify package version compatibility and security.

The added Radix UI Navigation Menu package version should be verified for compatibility with other Radix UI packages in the project.

✅ Verification successful

Version is compatible with other Radix UI packages

The added @radix-ui/react-navigation-menu@^1.2.4 is compatible with other Radix UI packages in the project as they all use the same major version (1.x.x). While a security audit couldn't be performed due to missing package-lock.json, the version is from a stable release of this well-maintained library.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check version compatibility with other Radix UI packages
echo "Installed Radix UI package versions:"
npm list | grep "@radix-ui"

# Check for known vulnerabilities
npm audit --json | jq '.advisories[] | select(.module_name | contains("@radix-ui/react-navigation-menu"))'

Length of output: 4304


Script:

#!/bin/bash
# Check if package-lock.json exists and get its content
if [ -f "package-lock.json" ]; then
    echo "=== Checking package-lock.json ==="
    # Extract all @radix-ui dependencies and their resolved versions
    jq '.packages | to_entries[] | select(.key | contains("@radix-ui")) | {name: .key, version: .value.version}' package-lock.json
    
    # Run npm audit if package-lock.json exists
    npm audit --json | jq '.advisories[] | select(.module_name | contains("@radix-ui"))'
else
    echo "package-lock.json not found"
fi

Length of output: 95

src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout/Flyout.stories.tsx Outdated Show resolved Hide resolved
@aralovelace aralovelace added the enhancement New feature or request label Jan 31, 2025
@aralovelace aralovelace marked this pull request as ready for review January 31, 2025 09:35
@aralovelace aralovelace marked this pull request as draft January 31, 2025 12:24
Copy link
Member

@jamiehenson jamiehenson left a comment

Choose a reason for hiding this comment

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

A great start, looking solid in Storybook already and you've made some good decisions with component structure. Left some comments mostly on code practice but below are some more product-oriented requests/suggestions ✏️

  • Your props are set out well and make sense, but I would change the ...Styling terminology to ...ClassName to be more consistent with other components and the React world in general (navMenuStyling can just be className as it's the outer-most container)
  • what you have is fine, but it'd be really cool if we could get the left-to-right animations from the Radix example working, there's more on it in the advanced animation section of the Radix docs. This is, unless Jamie W has specifically told you not to do this
  • we need a translucent underlay as well when the flyout is open, in the designs a 10% opacity neutral-1300 full screen box. Look into React portals to help you with this
  • we need to ensure that the flyout aligns with container edges, as is requested in the design (pic below). Worth adding another story with the container inside a div with ui-standard-container on it (as the name suggests, our standard container) and making sure those alignments work
    Screenshot 2025-01-31 at 12 09 25

tailwind.config.js Outdated Show resolved Hide resolved
tailwind.config.js Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
@aralovelace aralovelace force-pushed the WEB-4205_flyout branch 2 times, most recently from 85f60ad to b1a5833 Compare January 31, 2025 23:42
src/core/Flyout/Flyout.stories.tsx Outdated Show resolved Hide resolved
src/core/Flyout.tsx Outdated Show resolved Hide resolved
@aralovelace
Copy link
Contributor Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Feb 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (6)
src/core/Flyout.tsx (3)

62-67: Consider extracting default styles into constants.

The default styles are defined inline. Moving them to a separate constants file would improve maintainability and reusability.

Create a new file src/core/Flyout/constants.ts:

export const DEFAULT_MENU_LINK_STYLING = "ui-text-menu3 font-bold text-neutral-1000 dark:neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-1200 hover:text-neutral-1300 dark:hover:text-neutral-000 px-12 py-8 flex items-center justify-between";

export const DEFAULT_VIEWPORT_STYLING = "relative overflow-hidden w-full h-[var(--radix-navigation-menu-viewport-height)] origin-[top_center] transition-[width,_height] duration-300 data-[state=closed]:animate-scale-out data-[state=open]:animate-scale-in sm:w-[var(--radix-navigation-menu-viewport-width)]";

export const PANEL_ANIMATION = "data-[motion=from-end]:animate-enter-from-right data-[motion=from-start]:animate-enter-from-left data-[motion=to-end]:animate-exit-to-right data-[motion=to-start]:animate-exit-to-left";

69-77: Add fade-out animation to FlyOverlay.

The FlyOverlay component only has a fade-in animation. Adding a fade-out animation would improve the user experience.

-    className={cn(
-      "absolute left-0 right-0 h-screen w-full opacity-0 animate-[fadeInTenPercent_200ms_ease]",
-      className,
-    )}
+    className={cn(
+      "absolute left-0 right-0 h-screen w-full opacity-0",
+      "data-[state=open]:animate-[fadeInTenPercent_200ms_ease]",
+      "data-[state=closed]:animate-[fadeOutTenPercent_200ms_ease]",
+      className,
+    )}
+    data-state={isOpen ? "open" : "closed"}

Add the new animation to tailwind.config.js:

keyframes: {
  fadeOutTenPercent: {
    from: { opacity: 0.1 },
    to: { opacity: 0 },
  },
},
animation: {
  "fadeOutTenPercent": "fadeOutTenPercent 200ms ease",
}

79-136: Consider accessibility improvements.

The Flyout component could benefit from additional accessibility features.

Add ARIA labels and roles:

 <NavigationMenu
   className={cn(className, "flex w-full")}
   onValueChange={(val) => setIsOpen(!!val)}
+  aria-label="Main navigation menu"
 >
-  <NavigationMenuList className="flex list-none center">
+  <NavigationMenuList className="flex list-none center" role="menubar">
   {menuItems.map(({ label, content, link, panelStyling }) =>
     content ? (
-      <NavigationMenuItem key={label}>
+      <NavigationMenuItem key={label} role="menuitem">
         <NavigationMenuTrigger
           className={cn(
             "group outline-none focus:outline-none select-none cursor-pointer relative",
             DEFAULT_MENU_LINK_STYLING,
             menuLinkClassName,
           )}
+          aria-expanded="false"
         >
           {label}
         </NavigationMenuTrigger>
src/core/Flyout/Flyout.stories.tsx (2)

22-38: Consider adding keyboard navigation to ProductsGrid.

The ProductsGrid component handles mouse clicks but lacks keyboard navigation support.

Add keyboard navigation:

 const ProductsGrid = () => {
   const [selectedProduct, setSelectedProduct] = useState<ProductName | null>(
     null,
   );
   return (
     <div className="grid grid-cols-2">
       {Object.keys(products).map((product) => (
         <ProductTile
           name={product as ProductName}
           key={product}
           selected={selectedProduct === product}
           onClick={() => setSelectedProduct(product as ProductName)}
+          onKeyDown={(e) => {
+            if (e.key === 'Enter' || e.key === ' ') {
+              setSelectedProduct(product as ProductName);
+            }
+          }}
+          tabIndex={0}
+          role="button"
+          aria-pressed={selectedProduct === product}
         />
       ))}
     </div>
   );
 };

85-124: Add type safety to menuItems.

The menuItems array could benefit from stronger type safety.

Add a type definition:

type MenuItem = {
  label: string;
  content?: React.ReactNode;
  link?: string;
  panelStyling?: string;
};

const menuItems: MenuItem[] = [
  { label: "Home", content: null, link: "" },
  {
    label: "Products",
    content: <Panels panelLeft={<ProductsGrid />} platforms={platforms} />,
    panelStyling: panelStyling,
  },
  // ...
];
tailwind.config.js (1)

335-358: Follow kebab-case naming convention for animations.

The animation names should follow the kebab-case convention used by other animations in the file.

-        "scale-in": {
+        "scale-in-menu": {
           from: { opacity: "0", transform: "rotateX(-10deg) scale(0.9)" },
           to: { opacity: "1", transform: "rotateX(0deg) scale(1)" },
         },
-        "scale-out": {
+        "scale-out-menu": {
           from: { opacity: "1", transform: "rotateX(0deg) scale(1)" },
           to: { opacity: "0", transform: "rotateX(-10deg) scale(0.95)" },
         },
-        "enter-from-right": {
+        "enter-from-right-menu": {
           from: { opacity: "0", transform: "translateX(200px)" },
           to: { opacity: "1", transform: "translateX(0)" },
         },
-        "enter-from-left": {
+        "enter-from-left-menu": {
           from: { opacity: "0", transform: "translateX(-200px)" },
           to: { opacity: "1", transform: "translateX(0)" },
         },
-        "exit-to-right": {
+        "exit-to-right-menu": {
           from: { opacity: "1", transform: "translateX(0)" },
           to: { opacity: "0", transform: "translateX(200px)" },
         },
-        "exit-to-left": {
+        "exit-to-left-menu": {
           from: { opacity: "1", transform: "translateX(0)" },
           to: { opacity: "0", transform: "translateX(-200px)" },
         },

Update the animation references:

       animation: {
-        "scale-in": "scale-in 200ms ease",
-        "scale-out": "scale-out 200ms ease",
-        "enter-from-left": "enter-from-left 250ms ease",
-        "enter-from-right": "enter-from-right 250ms ease",
-        "exit-to-left": "exit-to-left 250ms ease",
-        "exit-to-right": "exit-to-right 250ms ease",
+        "scale-in-menu": "scale-in-menu 200ms ease",
+        "scale-out-menu": "scale-out-menu 200ms ease",
+        "enter-from-left-menu": "enter-from-left-menu 250ms ease",
+        "enter-from-right-menu": "enter-from-right-menu 250ms ease",
+        "exit-to-left-menu": "exit-to-left-menu 250ms ease",
+        "exit-to-right-menu": "exit-to-right-menu 250ms ease",
       },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7cb89 and 354b6d2.

⛔ Files ignored due to path filters (2)
  • src/core/Flyout/__snapshots__/Flyout.stories.tsx.snap is excluded by !**/*.snap
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • src/core/Flyout.tsx (1 hunks)
  • src/core/Flyout/Flyout.stories.tsx (1 hunks)
  • tailwind.config.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Run linters and tests
🔇 Additional comments (1)
src/core/Flyout.tsx (1)

17-60: Props type definition looks good.

The props are well-documented with JSDoc comments, making the component's API clear and easy to understand.

@aralovelace aralovelace marked this pull request as ready for review February 12, 2025 13:15
@aralovelace
Copy link
Contributor Author

@jamiehenson thanks for the review and very accurate recommendation which is very helpful for me. I have updated and let me know your thoughts. thank you

@aralovelace aralovelace requested a review from a team February 12, 2025 13:22
Copy link
Member

@jamiehenson jamiehenson left a comment

Choose a reason for hiding this comment

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

@aralovelace this looks pretty solid, thanks for taking the feedback in.

In the interests of moving this forward, I've advanced this with a commit that does a couple more things:

  • adds a fade-out transition to the flyout underlay on exit by deferring the unmounting of it by the length of the animation - a fairly useful trick for things like this. This has also been added to Header for consistency
  • added a delayDuration prop to NavigationMenu of 0 to remove the delay when hovering over a header item (we'd want it to be instant)
  • consolidated FlyOverlay inside Flyout as we'd want them to be paired together, correct me if I'm wrong but I don't see a situation where we'd use one without the other

Let me know if you have thoughts or questions about any of the above.

There's still the matter of constraining the Flyout positioning to the container - we have a container story here but I'm not sure what functionality it demonstrates. I'm not 100% on what we actually need here - it's worth checking with Jamie W about how we want to align the flyout with the page content (with the container, or with the browser window with an offset?)

Also, don't forget to bump the package.json version before you merge.

/**
* Optional styling for the flyout panel.
*/
panelStyling?: string;
Copy link
Member

Choose a reason for hiding this comment

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

This contradicts the ...ClassName conventions used elsewhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request review
Development

Successfully merging this pull request may close these issues.

2 participants