Skip to content

Select primitive t1#1117

Merged
kodiakhq[bot] merged 16 commits intorad-ui:mainfrom
GoldGroove06:select-primitive-t1
May 31, 2025
Merged

Select primitive t1#1117
kodiakhq[bot] merged 16 commits intorad-ui:mainfrom
GoldGroove06:select-primitive-t1

Conversation

@GoldGroove06
Copy link
Copy Markdown
Collaborator

@GoldGroove06 GoldGroove06 commented May 25, 2025

New Select primitive component
includes -

  • a11ly
  • narrator support
  • optional portal

new Select theme component

20250512-2058-21.8318537.mp4

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a customizable Select dropdown component with modular subcomponents for building advanced select menus.
    • Added support for controlled and uncontrolled selection, disabled options, initial values, and multiple instances.
    • Provided Storybook examples demonstrating various use cases and configurations.
    • Added a primitive Select component with context-driven state management and accessibility features.
  • Style

    • Added new styles for the Select component, including interactive states, animations, and accessibility enhancements.
  • Documentation

    • Included Storybook stories to showcase and document Select component usage and features.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2025

"""

Walkthrough

A new modular Select UI component system is introduced, including both a user-facing component and a primitive implementation with context, floating UI, and keyboard navigation. Supporting SCSS styles are added, and Storybook stories demonstrate usage and controlled/uncontrolled patterns. The styles are integrated into the default theme.

Changes

File(s) Change Summary
src/components/ui/Select/Select.tsx Added modular Select component exposing Root, Content, Item, Trigger, Portal, and Indicator subcomponents.
src/components/ui/Select/fragments/SelectContent.tsx
src/components/ui/Select/fragments/SelectItem.tsx
src/components/ui/Select/fragments/SelectRoot.tsx
src/components/ui/Select/fragments/SelectTrigger.tsx
src/components/ui/Select/fragments/SelectIndicator.tsx
src/components/ui/Select/fragments/SelectPortal.tsx
Added Select subcomponents: Content, Item, Root, Trigger, Indicator, Portal.
src/components/ui/Select/contexts/SelectRootContext.tsx Added SelectRootContext React context providing rootClass string.
src/components/ui/Select/stories/Select.stories.tsx Added Storybook stories for Select component, demonstrating various usage scenarios.
src/core/primitives/Select/Select.tsx Introduced SelectPrimitive namespace component with Root, Content, Portal, Overlay, Item, Trigger.
src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx Added context for SelectPrimitive state and handlers.
src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx
src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx
src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx
src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx
src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx
src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx
Added SelectPrimitive subcomponents: Content, Item, Overlay, Portal, Root, Trigger.
src/core/primitives/Select/stories/Select.stories.tsx Added Storybook stories for SelectPrimitive (basic and controlled usage).
styles/themes/components/select.scss Added SCSS styles for Select component, including root, trigger, content, item, indicator, and animations.
styles/themes/default.scss Imported Select component styles into the default theme.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SelectRoot
  participant SelectTrigger
  participant SelectContent
  participant SelectItem

  User->>SelectRoot: Render with props (value, onValueChange, ...)
  SelectRoot->>SelectTrigger: Render trigger button
  User->>SelectTrigger: Clicks trigger
  SelectTrigger->>SelectRoot: Toggle open state
  SelectRoot->>SelectContent: Render dropdown content if open
  SelectContent->>SelectItem: Render selectable items
  User->>SelectItem: Clicks item
  SelectItem->>SelectRoot: Calls onValueChange with selected value
  SelectRoot->>SelectTrigger: Update trigger display with new value
Loading

Possibly related PRs

Suggested labels

automerge

Suggested reviewers

  • kotAPI

Poem

🥕
In the garden of code, a new Select blooms,
With triggers and roots, and dropdowns that zoom.
Styled with care, it floats and it glides,
Options and stories, with context inside.
Now rabbits can choose, with a hop and a cheer—
The finest of dropdowns is finally here!
🐇
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-05-31T14_36_30_637Z-debug-0.log


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 90f8640 and 43e677f.

📒 Files selected for processing (7)
  • src/components/ui/AlertDialog/fragments/AlertDialogPortal.tsx (0 hunks)
  • src/components/ui/Select/Select.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectContent.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectIndicator.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectItem.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectTrigger.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • src/components/ui/AlertDialog/fragments/AlertDialogPortal.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/components/ui/Select/Select.tsx
  • src/components/ui/Select/fragments/SelectIndicator.tsx
  • src/components/ui/Select/fragments/SelectItem.tsx
  • src/components/ui/Select/fragments/SelectContent.tsx
  • src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx
  • src/components/ui/Select/fragments/SelectTrigger.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

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.

Copy link
Copy Markdown
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: 17

🧹 Nitpick comments (25)
src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx (3)

6-6: Remove unused import.

The clsx utility is imported but never used in this component.

-import { clsx } from 'clsx';

8-10: Consider removing unused className prop or implement it.

The className prop is defined but not used since the className application is commented out on line 18.

Either remove the unused prop:

-type SelectPrimitiveOverlayProps = {
-    className?: string;
-};
+type SelectPrimitiveOverlayProps = Record<string, never>;

Or implement it by uncommenting and fixing line 18:

-                    // className={clsx(`${rootClass}-overlay`, className)}
+                    className={clsx(`${rootClass}-overlay`, className)}

12-25: Simplify JSX structure.

The React fragments are unnecessary since you're only returning a single conditional element.

-function SelectPrimitiveOverlay({ className = '' }: SelectPrimitiveOverlayProps) {
+function SelectPrimitiveOverlay() {
     const { isOpen, handleOverlayClick } = useContext(SelectPrimitiveContext);
-    return (
-        <>
-            {isOpen && (
-                <Floater.Overlay
-                    // className={clsx(`${rootClass}-overlay`, className)}
-                    onClick={handleOverlayClick}
-                >
-                </Floater.Overlay>
-            )}
-        </>
-    );
+    return isOpen ? (
+        <Floater.Overlay onClick={handleOverlayClick} />
+    ) : null;
 }
src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx (1)

5-5: Remove unnecessary type assertion.

The type assertion as HTMLElement | null is redundant since document.body is already typed as HTMLElement | null.

-    const rootElement = document.querySelector('#rad-ui-theme-container') || document.body as HTMLElement | null;
+    const rootElement = document.querySelector('#rad-ui-theme-container') || document.body;
src/components/ui/Select/fragments/SelectRoot.tsx (2)

1-1: Remove unused React hook imports.

The useState and useCallback hooks are imported but never used in this component.

-import React, { useState, useCallback } from 'react';
+import React from 'react';

5-5: Consider updating COMPONENT_NAME for clarity.

The component name is SelectRoot but COMPONENT_NAME is set to 'Select'. Consider whether this should be more specific.

-const COMPONENT_NAME = 'Select';
+const COMPONENT_NAME = 'SelectRoot';

Or if the intention is to use the base component name for all Select fragments, consider adding a comment explaining this pattern.

src/components/ui/Select/fragments/SelectItem.tsx (1)

20-22: Consider extracting the SVG icon for reusability.

The checkmark SVG is hardcoded, which makes it difficult to customize or reuse across the component system.

Consider creating a separate icon component or using an icon library:

+import CheckIcon from '~/components/icons/CheckIcon';

-                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-                    <path d="M13.3332 4L5.99984 11.3333L2.6665 8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
-                </svg>
+                <CheckIcon />
src/components/ui/Select/fragments/SelectTrigger.tsx (1)

16-18: Remove unnecessary empty lines for cleaner code formatting.

The empty lines around the children don't add value and make the code less concise.

         >
-
             {children}
-
         </SelectPrimitive.Trigger>
src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx (2)

9-9: Add missing semicolon for consistent interface syntax.

The disabled property is missing a semicolon, which is inconsistent with TypeScript interface conventions.

-    disabled?: boolean
+    disabled?: boolean;

18-21: Improve code formatting for better readability.

The spacing and line breaks are inconsistent, making the code harder to read.

             <ButtonPrimitive
-                disabled={disabled} data-value={value}
-                aria-selected= {value === selectedValue}
-                onClick={() => handleSelect(value)} {...props}
+                disabled={disabled}
+                data-value={value}
+                aria-selected={value === selectedValue}
+                onClick={() => handleSelect(value)}
+                {...props}
                 {...getItemProps()}
src/components/ui/Select/fragments/SelectContent.tsx (1)

1-1: Remove unused imports.

The imports useEffect, useRef, and useContext are not used in this component and should be removed to keep the code clean.

-import React, { useEffect, useRef, useContext } from 'react';
+import React from 'react';
src/core/primitives/Select/stories/Select.stories.tsx (3)

5-5: Remove unused import.

The Floater import is not used in this file and should be removed.

-import Floater from '~/core/primitives/Floater';

44-44: Fix typo in trigger text.

There appears to be a typo in the trigger text - "helo" should likely be "hello" to match the BasicSelect story.

-            helo
+            hello

41-53: Consider consistency in Portal usage.

The ControlledExample doesn't use SelectPrimitive.Portal while BasicSelect does. This inconsistency might be intentional to demonstrate different usage patterns, but it could be confusing. Consider adding a comment explaining the difference or standardizing the approach.

src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1)

22-22: Consider safer context initialization.

Initializing the context with an empty object cast to the type could cause runtime errors if components try to access properties before the provider is set up. Consider providing default values or adding runtime checks.

-export const SelectPrimitiveContext = createContext<SelectPrimitiveContextType>({} as SelectPrimitiveContextType);
+export const SelectPrimitiveContext = createContext<SelectPrimitiveContextType | null>(null);

This would require components to check for null context and provide better error messages if the context is not properly set up.

src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx (1)

10-10: Remove commented out code.

The commented line should be removed if it's no longer needed, or if it's for debugging, consider adding a proper comment explaining its purpose.

-    // if (isOpen) return null;
src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx (4)

17-17: Improve code formatting for better readability.

-function SelectPrimitiveRoot({ children, className, value, defaultValue = '', onValueChange, onClickOutside = () => {}, ...props }: SelectPrimitiveRootProps) {
+function SelectPrimitiveRoot({ 
+    children, 
+    className, 
+    value, 
+    defaultValue = '', 
+    onValueChange, 
+    onClickOutside = () => {}, 
+    ...props 
+}: SelectPrimitiveRootProps) {

35-35: Remove commented code.

-    //   const click = Floater.useClick(context);

42-44: Clean up commented code in interactions array.

     const { getReferenceProps, getFloatingProps, getItemProps } = Floater.useInteractions([
-    // click,
         dismiss,
         role
     ]);

48-48: Break long line for better readability.

-    const values = { isOpen, setIsOpen, selectedValue, setSelectedValue, handleSelect, floatingContext, refs, getFloatingProps, getReferenceProps, floatingStyles, getItemProps };
+    const values = { 
+        isOpen, 
+        setIsOpen, 
+        selectedValue, 
+        setSelectedValue, 
+        handleSelect, 
+        floatingContext, 
+        refs, 
+        getFloatingProps, 
+        getReferenceProps, 
+        floatingStyles, 
+        getItemProps 
+    };
src/core/primitives/Select/Select.tsx (2)

8-11: Improve warning message with usage examples.

 const SelectPrimitive = () => {
-    console.warn('Direct usage of Select is not supported. Please use Select.Root, Select.Content, etc. instead.');
+    console.warn('SelectPrimitive should not be used directly. Use its subcomponents instead:\n' +
+        'Example: <SelectPrimitive.Root><SelectPrimitive.Trigger>...</SelectPrimitive.Trigger></SelectPrimitive.Root>');
     return null;
 };

8-20: Add TypeScript types for better type safety.

+type SelectPrimitiveType = {
+    (): null;
+    Root: typeof SelectPrimitiveRoot;
+    Content: typeof SelectPrimitiveContent;
+    Portal: typeof SelectPrimitivePortal;
+    Overlay: typeof SelectPrimitiveOverlay;
+    Item: typeof SelectPrimitiveItem;
+    Trigger: typeof SelectPrimitiveTrigger;
+};
+
-const SelectPrimitive = () => {
+const SelectPrimitive: SelectPrimitiveType = () => {
     console.warn('SelectPrimitive should not be used directly. Use its subcomponents instead:\n' +
         'Example: <SelectPrimitive.Root><SelectPrimitive.Trigger>...</SelectPrimitive.Trigger></SelectPrimitive.Root>');
     return null;
-};
+} as SelectPrimitiveType;
styles/themes/components/select.scss (3)

24-31: Ensure arrow pseudo-element doesn’t block pointer events
The &::after arrow will inherit pointer events, which may intercept clicks on the trigger. It’s best to explicitly disable pointer events on the pseudo-element.

 .rad-ui-select-trigger {
     &::after {
         content: '';
         border-style: solid;
         border-width: 5px 5px 0 5px;
         border-color: var(--rad-ui-color-accent-600) transparent transparent transparent;
         margin-left: 8px;
         transition: transform 0.2s ease;
+        pointer-events: none;
     }
 }

41-51: Consolidate focus styles to avoid duplication
Both :focus-visible and :focus blocks apply identical outline and box-shadow. To reduce repetition and potential drift, consider merging into a single selector (e.g. &:focus-visible, &:focus) or rely solely on :focus-visible for keyboard focus indicators.


224-246: Support reduced-motion preferences for animations
The keyframe animations enhance the UX, but consider respecting users’ prefers-reduced-motion settings by disabling or simplifying these animations under a media query.

@media (prefers-reduced-motion: reduce) {
  .rad-ui-select-content[data-state="open"],
  .rad-ui-select-content[data-state="closed"] {
    animation: none !important;
    transition: none !important;
  }
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1279c6d and f86c071.

📒 Files selected for processing (17)
  • src/components/ui/Select/Select.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectContent.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectItem.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectRoot.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectTrigger.tsx (1 hunks)
  • src/components/ui/Select/stories/Select.stories.tsx (1 hunks)
  • src/core/primitives/Select/Select.tsx (1 hunks)
  • src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx (1 hunks)
  • src/core/primitives/Select/stories/Select.stories.tsx (1 hunks)
  • styles/themes/components/select.scss (1 hunks)
  • styles/themes/default.scss (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx (1)
src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1)
  • SelectPrimitiveContext (22-22)
src/core/primitives/Select/stories/Select.stories.tsx (1)
src/components/ui/Select/stories/Select.stories.tsx (1)
  • ControlledExample (132-154)
src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx (1)
src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1)
  • SelectPrimitiveContext (22-22)
src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx (1)
src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1)
  • SelectPrimitiveContext (22-22)
src/components/ui/Select/stories/Select.stories.tsx (1)
src/core/primitives/Select/stories/Select.stories.tsx (1)
  • ControlledExample (36-60)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (9)
styles/themes/default.scss (1)

32-32: LGTM! Import follows the established pattern.

The select component styles import is correctly added and maintains alphabetical ordering with other component imports.

src/components/ui/Select/fragments/SelectItem.tsx (1)

15-16: Good accessibility implementation.

The component properly implements ARIA attributes including role="option" and aria-disabled, which ensures screen reader compatibility and follows accessibility best practices.

src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx (1)

12-27: Well-structured primitive component with proper context usage.

The component correctly uses the SelectPrimitiveContext and implements proper accessibility with aria-selected. The integration with RovingFocusGroup and ButtonPrimitive follows good composition patterns.

src/components/ui/Select/Select.tsx (2)

8-11: Excellent pattern for enforcing modular component usage.

The warning message and null return effectively prevent direct usage while clearly guiding developers to use the intended subcomponents. This promotes better component composition patterns.


13-16: Clean modular architecture with static property pattern.

The static property assignment pattern provides an intuitive API for accessing subcomponents while maintaining proper module boundaries. This approach aligns well with popular component libraries like Radix UI.

styles/themes/components/select.scss (4)

1-6: Appropriate scoping and default sizing for the root element
The .rad-ui-select-root block is well scoped, using inline-block for flexible placement and exposing a sensible default width via CSS variables. This sets a good foundation for theming and layout overrides.


114-194: Item styling is comprehensive and accessible
The .rad-ui-select-item rules cover font sizing, spacing, disabled states, hover/focus/selected interactions, and ARIA roles. They follow best practices for keyboard navigation and accessibility.


196-202: Label styling is clear and semantically appropriate
The .rad-ui-select-label block uses an uppercase, small-font design that clearly distinguishes section labels. This is a solid, self-contained pattern.


207-221: Icon and text flex layout is well-structured
The .rad-ui-select-icon and .rad-ui-select-text blocks correctly use flex properties for alignment and ellipsis overflow on text, ensuring a polished trigger appearance.


const COMPONENT_NAME = 'Select';

function SelectRoot({ customRootClass, children, defaultValue, value, onValueChange, ...props }: any) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety by replacing 'any' with proper types.

Using any type reduces type safety and IDE support. Consider defining proper prop types.

+type SelectRootProps = {
+    customRootClass?: string;
+    children?: React.ReactNode;
+    defaultValue?: string;
+    value?: string;
+    onValueChange?: (value: string) => void;
+} & React.ComponentPropsWithoutRef<'div'>;

-function SelectRoot({ customRootClass, children, defaultValue, value, onValueChange, ...props }: any) {
+function SelectRoot({ customRootClass, children, defaultValue, value, onValueChange, ...props }: SelectRootProps) {
🤖 Prompt for AI Agents
In src/components/ui/Select/fragments/SelectRoot.tsx at line 7, replace the use
of 'any' for the function props with a properly defined TypeScript interface or
type that explicitly lists all expected props and their types, such as
customRootClass as string or undefined, children as React.ReactNode,
defaultValue and value as appropriate types, and onValueChange as a function
type. This will improve type safety and enhance IDE support.


const COMPONENT_NAME = 'Select';

function SelectItem({ customRootClass, children, value, disabled, ...props }: any) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace any type with proper TypeScript interface for better type safety.

Using any eliminates TypeScript's type checking benefits and reduces IDE support for autocompletion and error detection.

-function SelectItem({ customRootClass, children, value, disabled, ...props }: any) {
+interface SelectItemProps {
+    customRootClass?: string;
+    children: React.ReactNode;
+    value: string;
+    disabled?: boolean;
+    [key: string]: any; // for additional props
+}
+
+function SelectItem({ customRootClass, children, value, disabled, ...props }: SelectItemProps) {
📝 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
function SelectItem({ customRootClass, children, value, disabled, ...props }: any) {
interface SelectItemProps {
customRootClass?: string;
children: React.ReactNode;
value: string;
disabled?: boolean;
[key: string]: any; // for additional props
}
function SelectItem(
{ customRootClass, children, value, disabled, ...props }: SelectItemProps
) {
// ...
}
🤖 Prompt for AI Agents
In src/components/ui/Select/fragments/SelectItem.tsx at line 7, replace the use
of the `any` type for the function parameter with a properly defined TypeScript
interface that specifies the expected props such as customRootClass, children,
value, disabled, and any other relevant properties. Define this interface above
the function or in a separate types file, then use it to type the props
parameter to improve type safety and enable better IDE support.


const COMPONENT_NAME = 'Select';

function SelectTrigger({ customRootClass, children, disabled, placeholder, ...props }: any) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace any type with proper TypeScript interface for consistency and type safety.

Similar to SelectItem, this component should use a proper TypeScript interface to maintain type safety across the Select component system.

-function SelectTrigger({ customRootClass, children, disabled, placeholder, ...props }: any) {
+interface SelectTriggerProps {
+    customRootClass?: string;
+    children: React.ReactNode;
+    disabled?: boolean;
+    placeholder?: string;
+    [key: string]: any;
+}
+
+function SelectTrigger({ customRootClass, children, disabled, placeholder, ...props }: SelectTriggerProps) {
🤖 Prompt for AI Agents
In src/components/ui/Select/fragments/SelectTrigger.tsx at line 7, replace the
use of the `any` type in the function parameter with a properly defined
TypeScript interface that specifies the expected props such as customRootClass,
children, disabled, placeholder, and any other relevant properties. Define this
interface above the function or import it if it exists, then use it to type the
props parameter to ensure type safety and consistency with other Select
components like SelectItem.

Comment thread src/components/ui/Select/Select.tsx Outdated

const COMPONENT_NAME = 'Select';

function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety by defining proper prop types.

The component uses any type for props, which eliminates type checking benefits. Consider defining a proper interface for the component props.

+interface SelectContentProps {
+  customRootClass?: string;
+  children: React.ReactNode;
+  position?: 'popper' | 'item-aligned';
+  [key: string]: any; // for additional props to spread
+}
+
-function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
+function SelectContent({ customRootClass, children, position = 'popper', ...props }: SelectContentProps) {
📝 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
function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
interface SelectContentProps {
customRootClass?: string;
children: React.ReactNode;
position?: 'popper' | 'item-aligned';
[key: string]: any; // for additional props to spread
}
function SelectContent({ customRootClass, children, position = 'popper', ...props }: SelectContentProps) {
// ...rest of the implementation
}
🤖 Prompt for AI Agents
In src/components/ui/Select/fragments/SelectContent.tsx at line 7, the
SelectContent component uses the 'any' type for its props, which disables type
checking. Define a proper TypeScript interface for the component props
specifying types for customRootClass, children, position, and any other props,
then replace 'any' with this interface to improve type safety.

Comment thread src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx Outdated
Comment thread src/components/ui/Select/stories/Select.stories.tsx
Comment thread src/components/ui/Select/stories/Select.stories.tsx Outdated
Comment on lines +132 to +154
export const ControlledExample = () => {
const [value, setValue] = React.useState('');
return (
<SandboxEditor>
<div className="w-[240px]">
<Select.Root defaultValue="option1" value={value} onValueChange={setValue}>
<Select.Trigger>
helo
</Select.Trigger>
<Select.Content>
<Select.Item value='option1'>Option 1</Select.Item>
<Select.Item value='option2'>Option 2</Select.Item>
<Select.Item value='option3'>Option 3</Select.Item>
</Select.Content>
</Select.Root>
</div>

<div className='mt-32'>
<p className='text-gray-950'>Value: {value}</p>
</div>
</SandboxEditor>
);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix conflicting props and state in controlled example.

The controlled example has several issues:

  1. Both defaultValue and value props are used together, which can cause conflicts
  2. State is initialized with empty string but defaultValue is "option1"
 export const ControlledExample = () => {
-    const [value, setValue] = React.useState('');
+    const [value, setValue] = React.useState('option1');
     return (
         <SandboxEditor>
             <div className="w-[240px]">
-                <Select.Root defaultValue="option1" value={value} onValueChange={setValue}>
+                <Select.Root value={value} onValueChange={setValue}>
                     <Select.Trigger>
-            helo
+                        <span>Select an option</span>
                     </Select.Trigger>
📝 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
export const ControlledExample = () => {
const [value, setValue] = React.useState('');
return (
<SandboxEditor>
<div className="w-[240px]">
<Select.Root defaultValue="option1" value={value} onValueChange={setValue}>
<Select.Trigger>
helo
</Select.Trigger>
<Select.Content>
<Select.Item value='option1'>Option 1</Select.Item>
<Select.Item value='option2'>Option 2</Select.Item>
<Select.Item value='option3'>Option 3</Select.Item>
</Select.Content>
</Select.Root>
</div>
<div className='mt-32'>
<p className='text-gray-950'>Value: {value}</p>
</div>
</SandboxEditor>
);
};
export const ControlledExample = () => {
const [value, setValue] = React.useState('option1');
return (
<SandboxEditor>
<div className="w-[240px]">
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger>
<span>Select an option</span>
</Select.Trigger>
<Select.Content>
<Select.Item value='option1'>Option 1</Select.Item>
<Select.Item value='option2'>Option 2</Select.Item>
<Select.Item value='option3'>Option 3</Select.Item>
</Select.Content>
</Select.Root>
</div>
<div className='mt-32'>
<p className='text-gray-950'>Value: {value}</p>
</div>
</SandboxEditor>
);
};
🤖 Prompt for AI Agents
In src/components/ui/Select/stories/Select.stories.tsx around lines 132 to 154,
the ControlledExample component incorrectly uses both defaultValue and value
props on Select.Root, causing conflicts, and initializes state with an empty
string while defaultValue is "option1". Remove the defaultValue prop from
Select.Root and initialize the state with "option1" to ensure the component is
properly controlled and consistent.

Comment thread styles/themes/components/select.scss Outdated
Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (3)
src/components/ui/Select/fragments/SelectItem.tsx (1)

5-5: Replace any type with proper TypeScript interface for better type safety.

Using any eliminates TypeScript's type checking benefits and reduces IDE support for autocompletion and error detection.

The past review comment correctly identified this type safety issue. Please implement the suggested TypeScript interface to replace the any type.

src/components/ui/Select/fragments/SelectTrigger.tsx (1)

6-6: Replace any type with proper TypeScript interface for consistency and type safety.

Similar to SelectItem, this component should use a proper TypeScript interface to maintain type safety across the Select component system.

The past review comment correctly identified this type safety issue. Please implement the suggested TypeScript interface as recommended.

src/components/ui/Select/fragments/SelectContent.tsx (1)

5-5: Address the type safety issue identified in previous review.

This is the same type safety concern raised in the previous review. The component still uses any type for props, which eliminates TypeScript's type checking benefits.

🧹 Nitpick comments (2)
src/components/ui/Select/fragments/SelectContent.tsx (2)

1-1: Remove unused imports.

The useEffect and useRef hooks are imported but never used in this component. Clean up the imports to only include what's necessary.

-import React, { useEffect, useRef, useContext } from 'react';
+import React, { useContext } from 'react';

7-18: Clean up JSX formatting.

The empty lines within the JSX structure (lines 12 and 15) are unnecessary and make the component less compact.

    return (
        <SelectPrimitive.Content
            className={`${rootClass}-content`}
            position={position}
            data-position={position}
-
            {...props}
        >
-
            {children}
        </SelectPrimitive.Content>
    );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 112e855 and e438333.

📒 Files selected for processing (10)
  • src/components/ui/Select/Select.tsx (1 hunks)
  • src/components/ui/Select/contexts/SelectRootContext.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectContent.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectIndicator.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectItem.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectRoot.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectTrigger.tsx (1 hunks)
  • src/components/ui/Select/stories/Select.stories.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/ui/Select/contexts/SelectRootContext.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx
  • src/components/ui/Select/fragments/SelectRoot.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx
  • src/components/ui/Select/stories/Select.stories.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/components/ui/Select/fragments/SelectIndicator.tsx (1)
src/components/ui/Select/contexts/SelectRootContext.tsx (1)
  • SelectRootContext (7-9)
src/components/ui/Select/fragments/SelectTrigger.tsx (1)
src/components/ui/Select/contexts/SelectRootContext.tsx (1)
  • SelectRootContext (7-9)
src/components/ui/Select/fragments/SelectContent.tsx (1)
src/components/ui/Select/contexts/SelectRootContext.tsx (1)
  • SelectRootContext (7-9)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (4)
src/components/ui/Select/fragments/SelectIndicator.tsx (1)

1-16: LGTM! Clean and well-structured indicator component.

The implementation is excellent with proper context usage, accessible SVG markup, and clean separation of concerns. The use of currentColor for the stroke ensures proper theming integration.

src/components/ui/Select/Select.tsx (1)

7-18: LGTM! Excellent modular component architecture.

The wrapper pattern with warning for direct usage and static subcomponent properties is a great architectural choice. This enforces proper usage of the modular API while providing a clean namespace for all Select-related components.

src/components/ui/Select/fragments/SelectItem.tsx (1)

8-21: LGTM! Excellent accessibility and component structure.

The implementation correctly uses accessibility attributes (role="option", aria-disabled, data-disabled) and follows good patterns for wrapping the primitive component while maintaining proper styling context.

src/components/ui/Select/fragments/SelectTrigger.tsx (1)

8-20: LGTM! Well-implemented trigger component with proper accessibility.

The implementation correctly uses accessibility attributes and follows good patterns for wrapping the primitive trigger while maintaining styling context and prop forwarding.

Comment on lines +5 to +19
function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
const { rootClass } = useContext(SelectRootContext);
return (
<SelectPrimitive.Content
className={`${rootClass}-content`}
position={position}
data-position={position}

{...props}
>

{children}
</SelectPrimitive.Content>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clarify the purpose of the customRootClass prop.

The customRootClass prop is destructured from the component props but is never used in the implementation. Either implement its intended functionality or remove it from the prop destructuring to avoid confusion.

If customRootClass should override the context-provided rootClass, consider this implementation:

function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
    const { rootClass } = useContext(SelectRootContext);
+    const effectiveRootClass = customRootClass || rootClass;
    return (
        <SelectPrimitive.Content
-            className={`${rootClass}-content`}
+            className={`${effectiveRootClass}-content`}
            position={position}
            data-position={position}
            {...props}
        >
            {children}
        </SelectPrimitive.Content>
    );
}

Alternatively, if it's not needed, remove it from the props:

-function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
+function SelectContent({ children, position = 'popper', ...props }: any) {
📝 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
function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
const { rootClass } = useContext(SelectRootContext);
return (
<SelectPrimitive.Content
className={`${rootClass}-content`}
position={position}
data-position={position}
{...props}
>
{children}
</SelectPrimitive.Content>
);
}
function SelectContent({ customRootClass, children, position = 'popper', ...props }: any) {
const { rootClass } = useContext(SelectRootContext);
const effectiveRootClass = customRootClass || rootClass;
return (
<SelectPrimitive.Content
className={`${effectiveRootClass}-content`}
position={position}
data-position={position}
{...props}
>
{children}
</SelectPrimitive.Content>
);
}
🤖 Prompt for AI Agents
In src/components/ui/Select/fragments/SelectContent.tsx around lines 5 to 19,
the customRootClass prop is destructured but never used, causing confusion.
Either remove customRootClass from the component's props if it is unnecessary,
or update the implementation to use customRootClass to override the rootClass
from context by applying customRootClass in the className instead of rootClass.
Ensure the className reflects the correct root class based on whether
customRootClass is provided.

Comment thread src/components/ui/AlertDialog/fragments/AlertDialogPortal.tsx Outdated
Comment thread src/components/ui/Select/Select.tsx
Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (4)
src/components/ui/AlertDialog/fragments/AlertDialogPortal.tsx (1)

13-13: Remove debug console.log statement.

This debug statement should be removed from production code.

src/components/ui/Select/stories/Select.stories.tsx (3)

116-116: Fix conflicting props in controlled example.

Using both defaultValue and value props together can cause conflicts in controlled components. The state should be initialized to match the default value or the default value should be removed.


118-118: Fix typo in trigger content.

The text "helo" appears to be a typo and should be corrected to meaningful placeholder text.


112-112: Fix inconsistent state initialization.

The state is initialized with an empty string while defaultValue is set to "option1", creating inconsistency.

🧹 Nitpick comments (10)
src/components/ui/Select/fragments/SelectPortal.tsx (2)

2-2: Remove unused import.

The useContext import is not used in this component.

-import React, { useContext } from 'react';
+import React from 'react';

10-10: Remove unused variable.

The rootElement variable is calculated but not used since SelectPrimitive.Portal handles its own root resolution.

-    const rootElement = document.querySelector('#rad-ui-theme-container') || document.body as HTMLElement | null;
styles/themes/components/select.scss (8)

22-32: Add smooth transition to the dropdown arrow
The arrow ::after content rotates when the dropdown opens, but there’s no transition defined. Adding a transition (e.g., transition: transform 0.2s ease) to the &::after block will make the rotation smoother.

.rad-ui-select-trigger {
  &::after {
    content: '';
    border-style: solid;
    border-width: 5px 5px 0 5px;
    border-color: var(--rad-ui-color-accent-600) transparent transparent transparent;
    margin-left: 8px;
+   transition: transform 0.2s ease;
  }

  &[data-state="open"]::after {
    transform: rotate(180deg);
  }
}

38-46: Combine duplicate focus and focus-visible styles
The &:focus-visible and &:focus blocks repeat the same outline and box-shadow rules. You can merge them to reduce duplication:

.rad-ui-select-trigger {
- &:focus-visible {
-   outline: none;
-   box-shadow: 0 0 0 2px var(--rad-ui-color-accent-600);
- }
-
- &:focus {
-   outline: none;
-   box-shadow: 0 0 0 2px var(--rad-ui-color-accent-600);
- }
+ &:focus, &:focus-visible {
+   outline: none;
+   box-shadow: 0 0 0 2px var(--rad-ui-color-accent-600);
+ }
}

66-83: Refine overflow declarations for clarity
Using overflow: hidden; followed by overflow-y: auto; can be misleading. Replace the general overflow rule with overflow-x: hidden; to explicitly target horizontal overflow:

.rad-ui-select-content {
-  overflow: hidden;
-  /* ... */
-  overflow-y: auto;
+  overflow-x: hidden;
+  /* ... */
+  overflow-y: auto;
}

116-125: Merge duplicate focus states for items
In .rad-ui-select-item, the &:focus-visible and &:focus selectors apply identical styles. Consolidate them to adhere to DRY:

.rad-ui-select-item {
- &:focus-visible {
-   outline: none;
-   background-color: var(--rad-ui-color-accent-300);
-   box-shadow: 0 0 0 2px var(--rad-ui-color-accent-500) inset;
- }
-
- &:focus {
-   outline: none;
-   background-color: var(--rad-ui-color-accent-300);
- }
+ &:focus, &:focus-visible {
+   outline: none;
+   background-color: var(--rad-ui-color-accent-300);
+   box-shadow: 0 0 0 2px var(--rad-ui-color-accent-500) inset;
+ }
}

138-150: Simplify selected-item indicator specificity
You target .rad-ui-select-item-indicator inside the [aria-selected] state. Since you already have an &-indicator block, nest it under the selected state to reduce selector verbosity:

.rad-ui-select-item {
  /* ... */
- &[aria-selected="true"] .rad-ui-select-item-indicator {
-   opacity: 1;
- }
+ &[aria-selected="true"] {
+   &-indicator {
+     opacity: 1;
+   }
+ }
}

161-167: Normalize indentation for label selector
The .rad-ui-select-label block is indented differently than other top-level selectors. Aligning it to column zero improves readability:

-     .rad-ui-select-label {
+ .rad-ui-select-label {

169-176: Normalize indentation for icon selector
Similarly, .rad-ui-select-icon should be aligned with other root-level classes for consistency:

-     .rad-ui-select-icon {
+ .rad-ui-select-icon {

178-183: Normalize indentation for text selector
The .rad-ui-select-text block also has extra indentation. Outdent it to match the project’s top-level style convention:

-     .rad-ui-select-text {
+ .rad-ui-select-text {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e438333 and 90f8640.

📒 Files selected for processing (13)
  • src/components/ui/AlertDialog/fragments/AlertDialogPortal.tsx (1 hunks)
  • src/components/ui/Select/Select.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectPortal.tsx (1 hunks)
  • src/components/ui/Select/fragments/SelectTrigger.tsx (1 hunks)
  • src/components/ui/Select/stories/Select.stories.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx (1 hunks)
  • styles/themes/components/select.scss (1 hunks)
  • styles/themes/default.scss (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • styles/themes/default.scss
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/components/ui/Select/fragments/SelectTrigger.tsx
  • src/components/ui/Select/Select.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveTrigger.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveOverlay.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveRoot.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/ui/Select/stories/Select.stories.tsx (1)
src/core/primitives/Select/stories/Select.stories.tsx (1)
  • ControlledExample (36-60)
src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx (1)
src/core/primitives/Select/contexts/SelectPrimitiveContext.tsx (1)
  • SelectPrimitiveContext (22-22)
🔇 Additional comments (1)
styles/themes/components/select.scss (1)

1-5: .rad-ui-select-root: baseline styles
Root container styling is well-structured—positioning, inline-block display, and width fallback using a CSS variable provide good flexibility.

Comment thread src/core/primitives/Select/fragments/SelectPrimitivePortal.tsx Outdated
@kotAPI kotAPI added the automerge A tag that tells kodiak bot to automerge PRs for us when tests and approval conditions are met label May 31, 2025
@kodiakhq kodiakhq Bot merged commit c3c09c5 into rad-ui:main May 31, 2025
5 checks passed
This was referenced Jul 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge A tag that tells kodiak bot to automerge PRs for us when tests and approval conditions are met

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants