-
Notifications
You must be signed in to change notification settings - Fork 0
chore: Tailwind Important with .dokan-layout added (Resolves Dokan Plugin Style Conflict)
#110
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
base: develop
Are you sure you want to change the base?
Conversation
WalkthroughUpdates package dependencies, adds scoped Tailwind preflight, exposes Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Modal as ModalComponent
participant Backdrop as DialogBackdrop
User->>Modal: Open modal (isOpen = true)
Modal->>Backdrop: render with default classes + `backdropClassName`
Note right of Backdrop: backdrop includes transitions\nand onClick that calls onClose
User->>Backdrop: click
Backdrop->>Modal: call onClose()
Modal->>User: modal closes (isOpen = false)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Poem
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this 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 (1)
src/components/Modal.tsx (1)
29-34: Consider adding type safety for backdrop classes.While the implementation is correct, consider using a union type for common backdrop class combinations to prevent invalid class combinations.
+type BackdropClass = 'backdrop-blur-lg' | 'backdrop-blur-sm' | 'backdrop-blur-none'; + export type ModalProps = { children: React.ReactNode; className?: string; - backdropClassName?: string; + backdropClassName?: BackdropClass | string; isOpen: boolean; showXButton?: boolean; onClose: () => void; } & HTMLAttributes<HTMLDivElement>;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package.json(3 hunks)src/components/Modal.tsx(2 hunks)tailwind.config.ts(2 hunks)
🔇 Additional comments (8)
tailwind.config.ts (3)
3-8: LGTM! Good choice of scoping strategy.The addition of
tailwindcss-scoped-preflightwith its isolation utilities is a good approach for preventing style leaks.
12-12: Verify the impact of usingimportantwith rootClass.While using
importantwith rootClass can help maintain style precedence within the scope, it might make it harder to override styles in specific cases.Consider testing edge cases where styles need to be overridden within the
.dokan-layoutscope.
55-60: LGTM! Well-structured plugin configuration.The plugin configuration correctly sets up style isolation using the rootClass scope.
src/components/Modal.tsx (2)
2-2: LGTM! Good upgrade to use DialogBackdrop.The switch to
DialogBackdropfromDialog.Overlayaligns with the latest Headless UI practices.
8-8: LGTM! Good addition of customization prop.The
backdropClassNameprop enhances the component's flexibility by allowing custom backdrop styling.package.json (3)
17-17: LGTM! Good addition of prepare script.The prepare script ensures the package is built before being used as a dependency.
76-76: LGTM! Good addition of scoped preflight dependency.The addition of
tailwindcss-scoped-preflightaligns with the style scoping strategy implemented in the Tailwind config.
31-33: Verify compatibility with Headless UI v2.The upgrade from v1 to v2 of @headlessui/react might introduce breaking changes.
❌ Verification inconclusive
🌐 Web query:
What are the breaking changes in @headlessui/react v2?💡 Result:
The transition to @headlessui/react v2 introduces several breaking changes from previous versions, primarily focused on improved accessibility, new component APIs, and React 18 compatibility:
React 18 Requirement
- Minimum React version bumped to 18.x[3][7][8], dropping support for older React versions
Component Changes
RadioGroup.Optiondeprecated in favor of newRadiocomponent[3][7]PopoverOverlayrenamed toPopoverBackdrop[3]- Dialog focus behavior now focuses dialog itself by default instead of first focusable element[3][7]
- New HTML structure for:
ListboxOptions/ListboxOptionComboboxOptions/ComboboxOptionTabGroup[3]Prop Changes
activeprop deprecated in favor offocusprop[3][7]- Form components now require explicit
nameprops instead of auto-generated IDs[1][3]- Transition components changed from class-based to data-attribute styling:
// Old (v1) <Transition enter="transition-opacity duration-75" enterFrom="opacity-0" enterTo="opacity-100" > // New (v2) <Transition className="transition-opacity duration-75 data-[closed]:opacity-0">Behavior Changes
- Disabled state handling now uses
data-disabledattributes instead of nativedisabledattribute[1][3]- Combobox virtualization requires using new
virtualprop instead of automatic detection[1][3]- Menu positioning now requires explicit
anchorprop configuration[1][3]TypeScript Changes
- Component types updated with stricter type constraints
- New
Reftypes required for form components[3][7]For migration help, refer to the official upgrade guide mentioned in [1] and check the detailed changelog entries in [3][7]. Projects using older patterns should audit their:
- Radio group implementations
- Dialog focus management
- Transition class names
- Form control markup
- Combobox virtualization logic
# Recommended upgrade path npm install @headlessui/react@latest --forceCitations:
- 1: https://tailwindcss.com/blog/headless-ui-v2
- 2: https://react-hot-toast.com/docs/version-2
- 3: https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md?plain=1
- 4: https://nextjs.org/blog/next-13-5
- 5: https://headlessui.com/react/transition
- 6: https://elixirforum.com/t/phoenix-liveview-headlessui/50030
- 7: https://github.com/tailwindlabs/headlessui/releases
- 8: https://www.npmjs.com/package/@headlessui/react
- 9: https://headlessui.com
Dependency Update Verified – Double Check Component Usages
The dependency update in your package.json (using
"@headlessui/react": "^2.2.0") correctly requires React 18 and aligns with the v2 upgrade. According to the web query, Headless UI v2 introduces several breaking changes (such as API updates for Radio Group components, renaming of PopoverOverlay to PopoverBackdrop, altered dialog focus behavior, and transition class changes).You should verify that any parts of your codebase using Headless UI components (e.g., RadioGroup, Dialog, Transition, etc.) have been updated according to the v2 guidelines. For instance:
- Replace deprecated or renamed components (e.g., use
Radioinstead ofRadioGroup.Optionif applicable).- Update component props like switching from
activetofocus.- Adjust any custom transition class names or HTML structuring as per the new API.
No issues were found in the dependency declaration, but please manually review your component implementations to ensure full compatibility with Headless UI v2.
# Conflicts: # package.json
There was a problem hiding this 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 (2)
package.json (2)
82-82: Tailwind Scoped Preflight Integration
The inclusion of"tailwindcss-scoped-preflight": "^3.4.9"is key to supporting improved style encapsulation, in line with the Tailwind configuration changes. However, if this package is only required during development or build-time, consider moving it todevDependenciesto optimize the production bundle.
25-86: Duplicate Dependency Declarations
It appears that some packages—specificallyreact-hot-toast,react-select, and@radix-ui/react-tooltip—are declared in bothdependenciesanddevDependencies. This duplication might lead to confusion or potential version conflicts. Please review these entries and consolidate them if they are not intentionally separated for runtime versus development needs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
package.json(3 hunks)
🔇 Additional comments (2)
package.json (2)
13-13: New Prepare Script Added
The addition of the"prepare": "vite build"script is appropriate for streamlining the build process. Please ensure that this script integrates well with your existing build pipeline and does not conflict with other build steps.
26-31: Updated Dependencies for UI Components and Runtime Libraries
The updated dependency declarations for@radix-ui/react-tooltip,@headlessui/react,react,react-dom,react-hot-toast, andreact-selectnow use a more flexible version specification. This change can improve compatibility across different environments. Please verify that these updates meet the requirements of all consumers and that no unintended side-effects occur.
* fix: `SimpleInput` left addon and right addon text overlapping * added build files * fix: simple radio static indigo color * fix: modal backdrop close * fix: build files
# Conflicts: # package.json # pnpm-lock.yaml # src/components/SimpleInput.tsx # src/stories/SimpleInput.stories.tsx
.dokan-layout added.dokan-layout added (Resolves Dokan Plugin Style Conflict)
There was a problem hiding this 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 (2)
src/components/AsyncSearchableSelect.tsx (2)
3-3: Consider using a more specific type for thecomponentsprop.The current typing
typeof componentsfrom react-select is very broad and may not provide adequate type safety. Consider defining a more specific type that only includes the components that can be overridden in AsyncSelect.-import { GroupBase, MenuPosition, components } from 'react-select'; +import { GroupBase, MenuPosition, ComponentsConfig } from 'react-select';- components?: typeof components; + components?: ComponentsConfig<Option, IsMulti, Group>;This would provide better type safety and IntelliSense support for users of the component.
Also applies to: 23-23
112-253: Consider extracting style builder logic to reduce repetition.The style building pattern is repeated 12+ times with identical structure. This could be refactored into a helper function to improve maintainability and reduce the chance of bugs.
Add a helper function at the top of the component:
const buildStyle = <T extends Record<string, any>>( base: T, userStyle: ((base: T, state: any) => T) | T | undefined, state: any, customStyles: T ): T => { const userControlStyles = typeof userStyle === 'function' ? userStyle(base, state) : userStyle || {}; return { ...base, ...customStyles, ...userControlStyles, }; };Then simplify each style handler:
- control: (base, state) => { - const userControl = props.styles?.control; - const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {}; - return { - ...base, - minHeight: '40px', - boxShadow: 'none', - borderRadius: '5px', - fontSize: '14px', - // Merge user-provided control styles (object or function result) - ...userControlStyles, - }; - }, + control: (base, state) => buildStyle( + base, + props.styles?.control, + state, + { + minHeight: '40px', + boxShadow: 'none', + borderRadius: '5px', + fontSize: '14px', + } + ),This pattern could be applied to all the style handlers to reduce duplication and improve maintainability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (7)
dist/components/AsyncSearchableSelect.d.tsis excluded by!**/dist/**dist/components/GooglePlaceAutocomplete.d.tsis excluded by!**/dist/**dist/components/SearchableSelect.d.tsis excluded by!**/dist/**dist/dokan-ui.cssis excluded by!**/dist/**dist/index.d.tsis excluded by!**/dist/**dist/index.jsis excluded by!**/dist/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
src/components/AsyncSearchableSelect.tsx(3 hunks)src/index.tsx(1 hunks)
🧰 Additional context used
🪛 ESLint
src/components/AsyncSearchableSelect.tsx
[error] 92-92: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.
(@typescript-eslint/ban-ts-comment)
🔇 Additional comments (1)
src/index.tsx (1)
47-47: LGTM! Export addition is well-placed and follows conventions.The re-export of
react-selectcomponents under theReactSelectnamespace is correctly positioned after theSelectcomponent export, maintaining the file's alphabetical organization pattern for component exports.
| ...( props?.classNames ? props.classNames : {} ), | ||
| control: (currentProps) => { | ||
| const userControl = props.classNames?.control; | ||
| // @ts-ignore | ||
| const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError: hasError }) : ''; | ||
| if (hasError && currentProps.isFocused) { | ||
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| if (isFocused) { | ||
| return '!border-2 !border-primary-500'; | ||
| if (currentProps.isFocused) { | ||
| return twMerge( '!border !ring-primary-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| if (hasError) { | ||
| return '!border !border-danger-500'; | ||
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| return '!border-gray-200'; | ||
| return twMerge( '!border-gray-200', userControlStyles ); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace @ts-ignore with @ts-expect-error for better type safety.
The TypeScript compiler directive should be more specific about the expected error.
- // @ts-ignore
+ // @ts-expect-error - hasError is not part of the original type but added for custom behavior
const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError: hasError }) : '';📝 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.
| ...( props?.classNames ? props.classNames : {} ), | |
| control: (currentProps) => { | |
| const userControl = props.classNames?.control; | |
| // @ts-ignore | |
| const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError: hasError }) : ''; | |
| if (hasError && currentProps.isFocused) { | |
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | |
| } | |
| if (isFocused) { | |
| return '!border-2 !border-primary-500'; | |
| if (currentProps.isFocused) { | |
| return twMerge( '!border !ring-primary-500 !ring-1', userControlStyles ); | |
| } | |
| if (hasError) { | |
| return '!border !border-danger-500'; | |
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | |
| } | |
| return '!border-gray-200'; | |
| return twMerge( '!border-gray-200', userControlStyles ); | |
| }, | |
| ...(props?.classNames ? props.classNames : {}), | |
| control: (currentProps) => { | |
| const userControl = props.classNames?.control; | |
| // @ts-expect-error - hasError is not part of the original type but added for custom behavior | |
| const userControlStyles = typeof userControl === 'function' | |
| ? userControl({ ...currentProps, hasError: hasError }) | |
| : ''; | |
| if (hasError && currentProps.isFocused) { | |
| return twMerge('!border !ring-danger-500 !ring-1', userControlStyles); | |
| } | |
| if (currentProps.isFocused) { | |
| return twMerge('!border !ring-primary-500 !ring-1', userControlStyles); | |
| } | |
| if (hasError) { | |
| return twMerge('!border !ring-danger-500 !ring-1', userControlStyles); | |
| } | |
| return twMerge('!border-gray-200', userControlStyles); | |
| }, |
🧰 Tools
🪛 ESLint
[error] 92-92: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.
(@typescript-eslint/ban-ts-comment)
🤖 Prompt for AI Agents
In src/components/AsyncSearchableSelect.tsx around lines 89 to 107, replace the
existing // @ts-ignore comment immediately above the userControlStyles
assignment with // @ts-expect-error to be explicit that the type error is
expected; ensure the comment sits directly above the line where
userControlStyles is computed and, if possible, narrow the type of userControl
(or cast it to unknown/any locally) so the expect-error only suppresses the
specific known issue rather than hiding other type problems.
…components and styles support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (1)
src/components/AsyncSearchableSelect.tsx (1)
90-109: Replace ts-ignore; keep extended state consciously.Switch to @ts-expect-error with a reason so eslint passes and intent is clear when adding hasError to the user control callback input.
- // @ts-ignore + // @ts-expect-error - passing hasError extends ClassNamesState for user styling const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError: hasError }) : '';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
dist/components/SearchableSelect.d.tsis excluded by!**/dist/**dist/index.jsis excluded by!**/dist/**
📒 Files selected for processing (2)
src/components/AsyncSearchableSelect.tsx(4 hunks)src/components/SearchableSelect.tsx(3 hunks)
🧰 Additional context used
🪛 ESLint
src/components/SearchableSelect.tsx
[error] 100-100: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 120-120: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 122-122: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 134-134: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 143-143: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 153-153: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 162-162: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 174-174: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
src/components/AsyncSearchableSelect.tsx
[error] 93-93: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.
(@typescript-eslint/ban-ts-comment)
[error] 113-113: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🔇 Additional comments (3)
src/components/AsyncSearchableSelect.tsx (1)
110-112: LGTM: menu portal passthrough.Passing menuPortalTarget and menuPosition through is correct and matches react-select’s API.
src/components/SearchableSelect.tsx (2)
5-5: LGTM: twMerge usage.Importing twMerge for class merging is appropriate here.
95-96: LGTM: user components override defaults.Spreading user components after local defaults gives consumers override control.
| import { CgSpinner } from 'react-icons/cg'; | ||
| import { FiChevronDown } from 'react-icons/fi'; | ||
| import { GroupBase, MenuPosition } from 'react-select'; | ||
| import { GroupBase, MenuPosition, components } from 'react-select'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use proper react-select types; drop unused value import.
Prefer Components, ClassNamesConfig, and StylesConfig types over typeof components. Also remove the unused value import of components.
Apply this diff:
-import { GroupBase, MenuPosition, components } from 'react-select';
+import { GroupBase, MenuPosition, Components, ClassNamesConfig, StylesConfig } from 'react-select';📝 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.
| import { GroupBase, MenuPosition, components } from 'react-select'; | |
| import { GroupBase, MenuPosition, Components, ClassNamesConfig, StylesConfig } from 'react-select'; |
🤖 Prompt for AI Agents
In src/components/AsyncSearchableSelect.tsx around line 3, the current import
pulls in the runtime 'components' value and uses typeof components for typing;
instead remove the unused runtime import and import the proper react-select
types: Components, ClassNamesConfig, and StylesConfig (and keep GroupBase,
MenuPosition). Update any type references that used typeof components to use the
Components generic type (e.g., Components<OptionType, IsMulti, GroupType>), and
replace any classNames/styles type usages with ClassNamesConfig and StylesConfig
respectively so the file uses proper react-select types rather than typeof
components.
| components?: typeof components; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Type the components prop as Partial<Components<...>>.
This aligns with react-select’s Components typing and enables proper generics.
- components?: typeof components;
+ components?: Partial<Components<Option, IsMulti, Group>>;📝 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.
| components?: typeof components; | |
| }; | |
| components?: Partial<Components<Option, IsMulti, Group>>; | |
| }; |
🤖 Prompt for AI Agents
In src/components/AsyncSearchableSelect.tsx around lines 23 to 24, replace the
loose typeof components typing with react-select's Components generic: change
components?: typeof components; to components?: Partial<Components<OptionType,
IsMulti, GroupType>> (ensure you import Components from 'react-select' and use
the same OptionType/IsMulti/GroupType generics used by the component), so the
prop matches react-select's Components typing and preserves proper generics.
| ...(props?.styles ? (props.styles as any) : {}), | ||
| control: (base, state) => { | ||
| const userControl = props.styles?.control; | ||
| const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove any from styles and type control slot correctly.
Use StylesConfig generics and handle function/object forms safely.
- styles={{
- ...(props?.styles ? (props.styles as any) : {}),
+ styles={{
+ ...(props?.styles ? (props.styles as StylesConfig<Option, IsMulti, Group>) : {}),
control: (base, state) => {
- const userControl = props.styles?.control;
- const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {};
+ const userControl = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.control;
+ const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : (userControl ?? {});📝 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.
| ...(props?.styles ? (props.styles as any) : {}), | |
| control: (base, state) => { | |
| const userControl = props.styles?.control; | |
| const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {}; | |
| ...(props?.styles ? (props.styles as StylesConfig<Option, IsMulti, Group>) : {}), | |
| control: (base, state) => { | |
| const userControl = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.control; | |
| const userControlStyles = typeof userControl === 'function' | |
| ? userControl(base, state) | |
| : (userControl ?? {}); |
🧰 Tools
🪛 ESLint
[error] 113-113: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🤖 Prompt for AI Agents
In src/components/AsyncSearchableSelect.tsx around lines 113–116, the code casts
props.styles to any and treats the control slot loosely; replace the any by
typing props.styles with react-select's StylesConfig generics (e.g.
StylesConfig<Option, IsMulti, Group>) on the component props, and type the
control slot as either a StylesConfig control function or style object. When
composing styles, detect if props.styles.control is a function then call it with
(base, state) and merge its return with your overrides, otherwise merge the
object directly; remove the (props.styles as any) cast and ensure the control
parameter types match react-select's ControlProps/state types so the
function/object forms are handled safely.
| @@ -1,7 +1,8 @@ | |||
| import Select, { GroupBase, MenuPosition, Props } from 'react-select'; | |||
| import Select, { GroupBase, MenuPosition, Props, components } from 'react-select'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Import react-select types instead of the value components.
Use Components, ClassNamesConfig, and StylesConfig; the value import components isn’t needed for typing.
-import Select, { GroupBase, MenuPosition, Props, components } from 'react-select';
+import Select, { GroupBase, MenuPosition, Props, Components, ClassNamesConfig, StylesConfig } from 'react-select';📝 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.
| import Select, { GroupBase, MenuPosition, Props, components } from 'react-select'; | |
| import Select, { GroupBase, MenuPosition, Props, Components, ClassNamesConfig, StylesConfig } from 'react-select'; |
🤖 Prompt for AI Agents
In src/components/SearchableSelect.tsx at line 1, the module currently imports
the runtime value `components` from 'react-select' but only needs type
definitions; replace that import with type imports for Components,
ClassNamesConfig, and StylesConfig (and any other needed react-select types) and
remove the runtime `components` import to avoid bundling the value; use `import
type { Components, ClassNamesConfig, StylesConfig, GroupBase, MenuPosition,
Props } from 'react-select'` (or equivalent) and update any type annotations to
reference these types.
| components?: typeof components; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Type the components prop as Partial<Components<...>>.
Aligns with react-select’s public API and fixes downstream type inference.
- components?: typeof components;
+ components?: Partial<Components<Option, IsMulti, Group>>;🤖 Prompt for AI Agents
In src/components/SearchableSelect.tsx around lines 21-22, the components prop
is currently typed incorrectly as typeof components; change it to be typed as
Partial of react-select's Components type using the same generics as the select
(i.e., the option type, isMulti flag and group type) so it matches
react-select's public API; also import Components from 'react-select' if not
already imported and ensure the prop type uses those generics to preserve
downstream type inference.
| ...(props?.classNames ? props.classNames : {}), | ||
| control: (currentProps) => { | ||
| const userControl = props.classNames?.control as any; | ||
| const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError }) : ''; | ||
|
|
||
| if (hasError && currentProps.isFocused) { | ||
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| if (isFocused) { | ||
| return '!border-2 !border-primary-500'; | ||
| if (currentProps.isFocused) { | ||
| return twMerge( '!border !ring-primary-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| if (hasError) { | ||
| return '!border !border-danger-500'; | ||
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | ||
| } | ||
|
|
||
| return '!border-gray-200'; | ||
| return twMerge( '!border-gray-200', userControlStyles ); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove any in classNames control; use ts-expect-error for extended state.
Type userControl with ClassNamesConfig and use @ts-expect-error when passing hasError.
classNames={{
...(props?.classNames ? props.classNames : {}),
control: (currentProps) => {
- const userControl = props.classNames?.control as any;
- const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError }) : '';
+ const userControl = props.classNames?.control as ClassNamesConfig<Option, IsMulti, Group>['control'] | undefined;
+ // @ts-expect-error - we intentionally pass hasError to extend ClassNamesState for user styling
+ const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError }) : '';
if (hasError && currentProps.isFocused) {
return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles );
}
if (currentProps.isFocused) {
return twMerge( '!border !ring-primary-500 !ring-1', userControlStyles );
}
if (hasError) {
return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles );
}
return twMerge( '!border-gray-200', userControlStyles );
},
}}📝 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.
| ...(props?.classNames ? props.classNames : {}), | |
| control: (currentProps) => { | |
| const userControl = props.classNames?.control as any; | |
| const userControlStyles = typeof userControl === 'function' ? userControl({ ...currentProps, hasError }) : ''; | |
| if (hasError && currentProps.isFocused) { | |
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | |
| } | |
| if (isFocused) { | |
| return '!border-2 !border-primary-500'; | |
| if (currentProps.isFocused) { | |
| return twMerge( '!border !ring-primary-500 !ring-1', userControlStyles ); | |
| } | |
| if (hasError) { | |
| return '!border !border-danger-500'; | |
| return twMerge( '!border !ring-danger-500 !ring-1', userControlStyles ); | |
| } | |
| return '!border-gray-200'; | |
| return twMerge( '!border-gray-200', userControlStyles ); | |
| }, | |
| ...(props?.classNames ? props.classNames : {}), | |
| control: (currentProps) => { | |
| - const userControl = props.classNames?.control as any; | |
| const userControl = props.classNames?.control as ClassNamesConfig<Option, IsMulti, Group>['control'] | undefined; | |
| // @ts-expect-error - we intentionally pass hasError to extend ClassNamesState for user styling | |
| const userControlStyles = typeof userControl === 'function' | |
| ? userControl({ ...currentProps, hasError }) | |
| : ''; | |
| if (hasError && currentProps.isFocused) { | |
| return twMerge('!border !ring-danger-500 !ring-1', userControlStyles); | |
| } | |
| if (currentProps.isFocused) { | |
| return twMerge('!border !ring-primary-500 !ring-1', userControlStyles); | |
| } | |
| if (hasError) { | |
| return twMerge('!border !ring-danger-500 !ring-1', userControlStyles); | |
| } | |
| return twMerge('!border-gray-200', userControlStyles); | |
| }, |
🧰 Tools
🪛 ESLint
[error] 100-100: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🤖 Prompt for AI Agents
In src/components/SearchableSelect.tsx around lines 98 to 116, the current code
types userControl as any and uses unknown "any" return handling; instead type
userControl using the proper ClassNamesConfig control type and avoid using any
in classNames control, and when calling the userControl function with the
extended state that includes hasError add a single-line @ts-expect-error above
that call to silence the type mismatch; keep using typeof userControl ===
'function' to decide invocation, pass { ...currentProps, hasError } to it, and
retain twMerge usage for combining returned styles.
| // Pass through any container-level custom styles if provided | ||
| ...(props?.styles ? (props.styles as any) : {}), | ||
| control: (base, state) => { | ||
| const userControl = props.styles?.control as any; | ||
| const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| minHeight: '40px', | ||
| boxShadow: 'none', | ||
| borderRadius: '5px', | ||
| fontSize: '14px', | ||
| ...userControlStyles, | ||
| }; | ||
| }, | ||
| option: (base, state) => { | ||
| const userOption = props.styles?.option as any; | ||
| const userOptionStyles = typeof userOption === 'function' ? userOption(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| fontSize: '0.875rem', | ||
| ...userOptionStyles, | ||
| }; | ||
| }, | ||
| multiValue: (base, state) => { | ||
| const userMultiValue = props.styles?.multiValue as any; | ||
| const userMultiValueStyles = typeof userMultiValue === 'function' ? userMultiValue(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| background: 'var(--colors-primary-50)', | ||
| borderRadius: '0.25rem', | ||
| ...userMultiValueStyles, | ||
| }; | ||
| }, | ||
| multiValueLabel: (base, state) => { | ||
| const userMultiValueLabel = props.styles?.multiValueLabel as any; | ||
| const userMultiValueLabelStyles = typeof userMultiValueLabel === 'function' ? userMultiValueLabel(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| color: 'var(--colors-primary-600)', | ||
| ...userMultiValueLabelStyles, | ||
| }; | ||
| }, | ||
| multiValueRemove: (base, state) => { | ||
| const userMultiValueRemove = props.styles?.multiValueRemove as any; | ||
| const userMultiValueRemoveStyles = typeof userMultiValueRemove === 'function' ? userMultiValueRemove(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| color: 'var(--colors-primary-600)', | ||
| ':hover': { | ||
| background: 'var(--colors-primary-100)', | ||
| }, | ||
| ...userMultiValueRemoveStyles, | ||
| }; | ||
| }, | ||
| option: (base) => ({ | ||
| ...base, | ||
| fontSize: '0.875rem', | ||
| }), | ||
| multiValue: (base) => ({ | ||
| ...base, | ||
| background: 'var(--colors-primary-50)', | ||
| borderRadius: '0.25rem', | ||
| }), | ||
| multiValueLabel: (base) => ({ | ||
| ...base, | ||
| color: 'var(--colors-primary-600)', | ||
| }), | ||
| multiValueRemove: (base) => ({ | ||
| ...base, | ||
| color: 'var(--colors-primary-600)', | ||
| ':hover': { | ||
| background: 'var(--colors-primary-100)', | ||
| }, | ||
| }), | ||
| menuPortal: (base) => ({ | ||
| ...base, | ||
| zIndex: 9999, | ||
| }), | ||
| menuPortal: (base, state) => { | ||
| const userMenuPortal = props.styles?.menuPortal as any; | ||
| const userMenuPortalStyles = typeof userMenuPortal === 'function' ? userMenuPortal(base, state) : {}; | ||
| return { | ||
| ...base, | ||
| zIndex: 9999, | ||
| ...userMenuPortalStyles, | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Eliminate explicit any in styles; use StylesConfig across slots.
Type-safe merge handles both function and object user styles.
styles={{
- // Pass through any container-level custom styles if provided
- ...(props?.styles ? (props.styles as any) : {}),
+ // Pass through any container-level custom styles if provided
+ ...(props?.styles ? (props.styles as StylesConfig<Option, IsMulti, Group>) : {}),
control: (base, state) => {
- const userControl = props.styles?.control as any;
- const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {};
+ const userControl = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.control;
+ const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : (userControl ?? {});
return {
...base,
minHeight: '40px',
boxShadow: 'none',
borderRadius: '5px',
fontSize: '14px',
...userControlStyles,
};
},
option: (base, state) => {
- const userOption = props.styles?.option as any;
- const userOptionStyles = typeof userOption === 'function' ? userOption(base, state) : {};
+ const userOption = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.option;
+ const userOptionStyles = typeof userOption === 'function' ? userOption(base, state) : (userOption ?? {});
return {
...base,
fontSize: '0.875rem',
...userOptionStyles,
};
},
multiValue: (base, state) => {
- const userMultiValue = props.styles?.multiValue as any;
- const userMultiValueStyles = typeof userMultiValue === 'function' ? userMultiValue(base, state) : {};
+ const userMultiValue = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValue;
+ const userMultiValueStyles = typeof userMultiValue === 'function' ? userMultiValue(base, state) : (userMultiValue ?? {});
return {
...base,
background: 'var(--colors-primary-50)',
borderRadius: '0.25rem',
...userMultiValueStyles,
};
},
multiValueLabel: (base, state) => {
- const userMultiValueLabel = props.styles?.multiValueLabel as any;
- const userMultiValueLabelStyles = typeof userMultiValueLabel === 'function' ? userMultiValueLabel(base, state) : {};
+ const userMultiValueLabel = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValueLabel;
+ const userMultiValueLabelStyles = typeof userMultiValueLabel === 'function' ? userMultiValueLabel(base, state) : (userMultiValueLabel ?? {});
return {
...base,
color: 'var(--colors-primary-600)',
...userMultiValueLabelStyles,
};
},
multiValueRemove: (base, state) => {
- const userMultiValueRemove = props.styles?.multiValueRemove as any;
- const userMultiValueRemoveStyles = typeof userMultiValueRemove === 'function' ? userMultiValueRemove(base, state) : {};
+ const userMultiValueRemove = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValueRemove;
+ const userMultiValueRemoveStyles = typeof userMultiValueRemove === 'function' ? userMultiValueRemove(base, state) : (userMultiValueRemove ?? {});
return {
...base,
color: 'var(--colors-primary-600)',
':hover': {
background: 'var(--colors-primary-100)',
},
...userMultiValueRemoveStyles,
};
},
menuPortal: (base, state) => {
- const userMenuPortal = props.styles?.menuPortal as any;
- const userMenuPortalStyles = typeof userMenuPortal === 'function' ? userMenuPortal(base, state) : {};
+ const userMenuPortal = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.menuPortal;
+ const userMenuPortalStyles = typeof userMenuPortal === 'function' ? userMenuPortal(base, state) : (userMenuPortal ?? {});
return {
...base,
zIndex: 9999,
...userMenuPortalStyles,
};
}
}}📝 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.
| // Pass through any container-level custom styles if provided | |
| ...(props?.styles ? (props.styles as any) : {}), | |
| control: (base, state) => { | |
| const userControl = props.styles?.control as any; | |
| const userControlStyles = typeof userControl === 'function' ? userControl(base, state) : {}; | |
| return { | |
| ...base, | |
| minHeight: '40px', | |
| boxShadow: 'none', | |
| borderRadius: '5px', | |
| fontSize: '14px', | |
| ...userControlStyles, | |
| }; | |
| }, | |
| option: (base, state) => { | |
| const userOption = props.styles?.option as any; | |
| const userOptionStyles = typeof userOption === 'function' ? userOption(base, state) : {}; | |
| return { | |
| ...base, | |
| fontSize: '0.875rem', | |
| ...userOptionStyles, | |
| }; | |
| }, | |
| multiValue: (base, state) => { | |
| const userMultiValue = props.styles?.multiValue as any; | |
| const userMultiValueStyles = typeof userMultiValue === 'function' ? userMultiValue(base, state) : {}; | |
| return { | |
| ...base, | |
| background: 'var(--colors-primary-50)', | |
| borderRadius: '0.25rem', | |
| ...userMultiValueStyles, | |
| }; | |
| }, | |
| multiValueLabel: (base, state) => { | |
| const userMultiValueLabel = props.styles?.multiValueLabel as any; | |
| const userMultiValueLabelStyles = typeof userMultiValueLabel === 'function' ? userMultiValueLabel(base, state) : {}; | |
| return { | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| ...userMultiValueLabelStyles, | |
| }; | |
| }, | |
| multiValueRemove: (base, state) => { | |
| const userMultiValueRemove = props.styles?.multiValueRemove as any; | |
| const userMultiValueRemoveStyles = typeof userMultiValueRemove === 'function' ? userMultiValueRemove(base, state) : {}; | |
| return { | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| ':hover': { | |
| background: 'var(--colors-primary-100)', | |
| }, | |
| ...userMultiValueRemoveStyles, | |
| }; | |
| }, | |
| option: (base) => ({ | |
| ...base, | |
| fontSize: '0.875rem', | |
| }), | |
| multiValue: (base) => ({ | |
| ...base, | |
| background: 'var(--colors-primary-50)', | |
| borderRadius: '0.25rem', | |
| }), | |
| multiValueLabel: (base) => ({ | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| }), | |
| multiValueRemove: (base) => ({ | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| ':hover': { | |
| background: 'var(--colors-primary-100)', | |
| }, | |
| }), | |
| menuPortal: (base) => ({ | |
| ...base, | |
| zIndex: 9999, | |
| }), | |
| menuPortal: (base, state) => { | |
| const userMenuPortal = props.styles?.menuPortal as any; | |
| const userMenuPortalStyles = typeof userMenuPortal === 'function' ? userMenuPortal(base, state) : {}; | |
| return { | |
| ...base, | |
| zIndex: 9999, | |
| ...userMenuPortalStyles, | |
| }; | |
| } | |
| styles={{ | |
| // Pass through any container-level custom styles if provided | |
| ...(props?.styles ? (props.styles as StylesConfig<Option, IsMulti, Group>) : {}), | |
| control: (base, state) => { | |
| const userControl = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.control; | |
| const userControlStyles = typeof userControl === 'function' | |
| ? userControl(base, state) | |
| : (userControl ?? {}); | |
| return { | |
| ...base, | |
| minHeight: '40px', | |
| boxShadow: 'none', | |
| borderRadius: '5px', | |
| fontSize: '14px', | |
| ...userControlStyles, | |
| }; | |
| }, | |
| option: (base, state) => { | |
| const userOption = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.option; | |
| const userOptionStyles = typeof userOption === 'function' | |
| ? userOption(base, state) | |
| : (userOption ?? {}); | |
| return { | |
| ...base, | |
| fontSize: '0.875rem', | |
| ...userOptionStyles, | |
| }; | |
| }, | |
| multiValue: (base, state) => { | |
| const userMultiValue = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValue; | |
| const userMultiValueStyles = typeof userMultiValue === 'function' | |
| ? userMultiValue(base, state) | |
| : (userMultiValue ?? {}); | |
| return { | |
| ...base, | |
| background: 'var(--colors-primary-50)', | |
| borderRadius: '0.25rem', | |
| ...userMultiValueStyles, | |
| }; | |
| }, | |
| multiValueLabel: (base, state) => { | |
| const userMultiValueLabel = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValueLabel; | |
| const userMultiValueLabelStyles = typeof userMultiValueLabel === 'function' | |
| ? userMultiValueLabel(base, state) | |
| : (userMultiValueLabel ?? {}); | |
| return { | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| ...userMultiValueLabelStyles, | |
| }; | |
| }, | |
| multiValueRemove: (base, state) => { | |
| const userMultiValueRemove = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.multiValueRemove; | |
| const userMultiValueRemoveStyles = typeof userMultiValueRemove === 'function' | |
| ? userMultiValueRemove(base, state) | |
| : (userMultiValueRemove ?? {}); | |
| return { | |
| ...base, | |
| color: 'var(--colors-primary-600)', | |
| ':hover': { | |
| background: 'var(--colors-primary-100)', | |
| }, | |
| ...userMultiValueRemoveStyles, | |
| }; | |
| }, | |
| menuPortal: (base, state) => { | |
| const userMenuPortal = (props.styles as StylesConfig<Option, IsMulti, Group> | undefined)?.menuPortal; | |
| const userMenuPortalStyles = typeof userMenuPortal === 'function' | |
| ? userMenuPortal(base, state) | |
| : (userMenuPortal ?? {}); | |
| return { | |
| ...base, | |
| zIndex: 9999, | |
| ...userMenuPortalStyles, | |
| }; | |
| } | |
| }} |
🧰 Tools
🪛 ESLint
[error] 120-120: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 122-122: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 134-134: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 143-143: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 153-153: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 162-162: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 174-174: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🤖 Prompt for AI Agents
In src/components/SearchableSelect.tsx around lines 119 to 181, the code uses
explicit any for user style slots; replace those anys with react-select's typed
StylesConfig and CSSObject types and type the props.styles accordingly (e.g.
import { StylesConfig, CSSObject } from 'react-select' and treat props.styles as
StylesConfig<OptionType, IsMulti>), then for each slot cast the slot from
props.styles to the correct union type (function or object) and handle both
cases in a type-safe way (if typeof slot === 'function' call it with (base,
state) to get a CSSObject, otherwise use the object directly), and update the
slot function signatures to return CSSObject so the merged style objects are
fully type-checked instead of using any.
There was a problem hiding this 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
🧹 Nitpick comments (4)
src/components/FormSelect.tsx (4)
16-17: Good addition: portalTarget prop enables scoped portalsEnables styling within a scoped root. Consider aligning the prop name with similar components (e.g.,
menuPortalTargetused in Async/Searchable Select) to keep API consistent across your Select variants.
61-63: Use a stable keyAvoid index keys to prevent reconciliation issues when options change order. Use the option value instead.
- key={index} + key={String(option.value)}
20-27: Preserve original value type in onChangeRadix Select emits strings. Your
onChangeAPI advertisesstring | numberbut currently forwards only strings. Map back to the original option to keep numbers as numbers.- onValueChange={(value) => { - onChange?.(value); - }} + onValueChange={(next) => { + const match = options.find((o) => String(o.value) === next); + onChange?.(match ? match.value : next); + }}
19-19: Prefer named export to match repo conventionsGuidelines specify named exports for components. If feasible, migrate to a named export to keep APIs consistent.
-export default function FormSelect(props: FormSelectProps) { +export function FormSelect(props: FormSelectProps) {Note: re-export/update imports where necessary.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
dist/components/FormSelect.d.tsis excluded by!**/dist/**dist/index.jsis excluded by!**/dist/**
📒 Files selected for processing (1)
src/components/FormSelect.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/{components,utils}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/{components,utils}/**/*.{ts,tsx}: Use TypeScript for all components and utilities
Use named exports for components and utilities
Files:
src/components/FormSelect.tsx
src/components/**/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/components/**/*.tsx: Use functional React components and hooks for all components
Use Tailwind CSS utility classes for component styling
Define and export TypeScript prop types/interfaces for each component
Files:
src/components/FormSelect.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{ts,tsx}: Use@as an alias forsrc/in imports
UsedebounceSearchfor debounced async search interactions
Files:
src/components/FormSelect.tsx
src/components/*.tsx
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Place new components at src/components/ComponentName.tsx
Files:
src/components/FormSelect.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: getdokan/dokan-ui#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-04T09:20:19.197Z
Learning: Use React 18+ features (update for React 19 as needed)
| </Select.Trigger> | ||
|
|
||
| <Select.Portal> | ||
| <Select.Portal container={props?.portalTarget || document.body}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid document.body default: SSR-unsafe and breaks scoped Tailwind (.dokan-layout)
- Directly referencing
document.bodyduring render can throw on SSR. - With Tailwind
important: '.dokan-layout', portaling tobodycauses utilities to miss the.dokan-layoutscope, producing unstyled menus unless consumers remember to passportalTarget.
Refactor to compute a safe default that prefers the nearest .dokan-layout when available, otherwise let Radix default to body.
Apply within this range:
- <Select.Portal container={props?.portalTarget || document.body}>
+ <Select.Portal container={portalContainer}>Add inside the component before the return (outside this hunk):
// Prefer consumer-provided target; otherwise, default to the scoped root if present.
// Falls back to undefined on SSR so Radix defaults safely.
let portalContainer: HTMLElement | undefined = props.portalTarget;
if (!portalContainer && typeof window !== 'undefined') {
const root = document.querySelector('.dokan-layout');
portalContainer = root instanceof HTMLElement ? root : document.body;
}🤖 Prompt for AI Agents
In src/components/FormSelect.tsx around line 45, avoid referencing document.body
directly in the JSX; instead, before the component return (outside the shown
hunk) add logic to compute a safe portalContainer that prefers
props.portalTarget, otherwise uses the nearest .dokan-layout when running in the
browser and falls back to document.body only on client (leaving undefined during
SSR). Then update the Select.Portal container prop to use this portalContainer
(so on SSR it passes undefined and Radix defaults safely, and on client it uses
the scoped root when present).
Important
This PR is for the Dokan Plugin Compatibility.
Please do not close or remove the Branch.
In this PR we are resolving a styling conflict for Tailwind BASE and other layers that take effect on customer websites' styling.
Summary by CodeRabbit
New Features
Style
Chores