Skip to content

Conversation

@amnambiar
Copy link
Collaborator

Pull request template

Description

Details of this SelectBox component and its behaviour is as per this document

Task:

Checklist

  • Commit messages broadly make sense and follow the Conventional Commits format.
  • PR targets master/develop
  • New tests are added for the changes (if not, mention it in the PR description). These may include:
    • Unit tests,
    • Integration tests,
    • Property-Based testing,
    • End-to-end tests.
  • The new version builds and passes all tests. If not, please mention it in the PR description.
  • Self review of the code has been done.
  • Reviewer has been requested.

@amnambiar amnambiar changed the base branch from main to nextjs-tailwind October 14, 2025 07:17
@amnambiar amnambiar requested a review from KJES4 October 14, 2025 07:17
label: string;
value: string;
type?: "standard";
status?: "passed" | "failed" | "pending";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's change status to match the status options for a property "valid" | "falsified" | "undetermined"

onChange={(val) => setSortValue(val as string)}
/>
<SelectBox
options={selectBoxGroupedOptions}
Copy link
Collaborator

Choose a reason for hiding this comment

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

On my machine I am seeing a ts error on this prop. Is that showing up on your end?

SelectBox.tsx(24, 5): The expected type comes from property 'options' which is declared here on type 'IntrinsicAttributes & SelectProps'

onChange: (values: string[]) => void;
}

const Select: React.FC<SelectProps> = ({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Using React.FC is a historically controversial way to define a component in React as such many project have moved away from doing this. As of TypeScript 5.1 and React 18, React.FC is now officially 'fine' but many project still do not use this. This way of defining a function also doesn't match the other ways functions were defined and it is important to keep code consistent across the codebase at times where it makes sense to do so. Is there a particular reason you chose to use React.FC here?

}) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [searchValue, setSearchValue] = useState<string>("");
const [selectedValues, setSelectedValues] = useState<string[]>([]); // values of slected options
Copy link
Collaborator

Choose a reason for hiding this comment

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

The state for the selectedValues is state that will impact more than just this component so it should be lifted up out of this component and passed through as a prop just like what was done in the dropdown component.

const chipsContainerRef = useRef<HTMLDivElement>(null);

const theme = {
textOnSurface: "text-gray-900 dark:text-gray-100",
Copy link
Collaborator

Choose a reason for hiding this comment

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

onSurface is already a CSS variable that is defined in the global.css file. It is further defined as a css token and it already has both a light and dark option defined. This doesn't need to be redefined in this file.


const theme = {
textOnSurface: "text-gray-900 dark:text-gray-100",
bgContainer: "bg-white dark:bg-gray-700",
Copy link
Collaborator

Choose a reason for hiding this comment

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

container is already a CSS variable that is defined in the global.css file. It is further defined as a css token and it already has both a light and dark option defined. This doesn't need to be redefined in this file.

const theme = {
textOnSurface: "text-gray-900 dark:text-gray-100",
bgContainer: "bg-white dark:bg-gray-700",
bgContainerHigh: "bg-gray-100 dark:bg-gray-800",
Copy link
Collaborator

Choose a reason for hiding this comment

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

containerHigh is already a CSS variable that is defined in the global.css file. It is further defined as a css token and it already has both a light and dark option defined. This doesn't need to be redefined in this file.

textOnSurface: "text-gray-900 dark:text-gray-100",
bgContainer: "bg-white dark:bg-gray-700",
bgContainerHigh: "bg-gray-100 dark:bg-gray-800",
borderOutline: "border-gray-300 dark:border-gray-600",
Copy link
Collaborator

Choose a reason for hiding this comment

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

outline is already a CSS variable that is defined in the global.css file. It is further defined as a css token and it already has both a light and dark option defined. This doesn't need to be redefined in this file.

const inputRef = useRef<HTMLInputElement>(null);
const chipsContainerRef = useRef<HTMLDivElement>(null);

const theme = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove this theme object and define all the styles inline, all but one of the style options in this theme options have already been defined in the global stylesheet or are only applied in one location.

};

return (
<div ref={componentRef} className="relative w-full font-inter">
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is the font set to inter here?

}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you used a 'click' event listener in dropdown why did you choose to use a 'mousedown' click event in this listener?

showAllSelected = false,
onChange = () => {},
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you name these state variables to [open, setOpen] that way we are ensuring consistent naming in all the components? The dropdown component has the naming for this similar state that I am requesting.

Copy link
Collaborator

@KJES4 KJES4 left a comment

Choose a reason for hiding this comment

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

I left some comments but this is still in review.

e.stopPropagation();
if (!isOpen) setIsOpen(true);
}}
placeholder={placeholderVisible ? placeholder : ""}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you explain to me what this conditional statement is for?

type="text"
value={inputDisplayValue}
onChange={(e) => setSearchValue(e.target.value)}
onClick={(e) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the parent element already handles changing the open state, why is this onClick event needed?

if (!isOpen) setIsOpen(true);
}}
placeholder={placeholderVisible ? placeholder : ""}
readOnly={!search || (!isOpen && !!selectedItem)}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please explain what functionality you are trying to achieve with this conditional statement?

options={selectBoxGroupedOptions}
placeholder="Select Properties"
search={true}
multiSelect={false}
Copy link
Collaborator

Choose a reason for hiding this comment

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

The default value for this prop is false so you don't need to redefine a value of false here as well.

<SelectBox
placeholder="Select Properties"
options={flatSelectBoxOptions}
multiSelect={false}
Copy link
Collaborator

Choose a reason for hiding this comment

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

The default value for this prop is false so you don't need to redefine a value of false here as well.

isOpen ? "rotate-180" : "rotate-0"
} ${theme.textOnSurface}`}
>
<ChevronDown />
Copy link
Collaborator

Choose a reason for hiding this comment

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

This icon isn't showing up in the UI for me.


// Focus input when opening and searchable
if (!isOpen && search && inputRef.current) {
setTimeout(() => inputRef.current?.focus(), 0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please explain what you are attempting to do with this conditional setTimeout statement?

@amnambiar
Copy link
Collaborator Author

Reworking on the PR to be in sync with changes of Dropdown component as in 8230865; updating and reusing Menu

@KJES4 KJES4 self-assigned this Nov 5, 2025
@amnambiar
Copy link
Collaborator Author

@KJES4 , I've updated the PR with a revised SelectBox. Do have a fresh look at it. Let me know if you still see something that needs to be modified.

A very wide summary

  • Corrected style classes
  • Menu-based integration
  • All variant demos on Page
  • Keyboard support

<Button
variant="embedded"
onMouseDown={handleClear}
content={hasError && !isFocused ? {svg: <ExclamationCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'error'} : { svg: <XCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'onVariant' }} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove the change in this doc. I added the size prop back to the nextjs-tailwind branch. The size prop needs to remain here or else it breaks the styling of the icons.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Understood 👍 Thanks for pointing it! Reverted.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I think this might have been a mistake on my end. I originally removed these styles then I added them back.

<Button
variant="embedded"
onMouseDown={handleClear}
content={hasError && !isFocused ? { svg: <ExclamationCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'error' } : {svg: <XCircleIcon />, mode: 'stroke', size: 'small', strokeWidth: 1.5, color: 'onVariant'}}/>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove the change in this doc. I added the size prop back to the nextjs-tailwind branch. The size prop needs to remain here or else it breaks the styling of the icons.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Understood 👍 Thanks for pointing it! Reverted.

}

const chipVariants = cva('inline-flex items-center justify-center rounded-md text-xs font-medium whitespace-nowrap p-2 text-onVariant/90 hover:text-onSurface focus-within:ring-1 focus-within:ring-primary', {
const chipVariants = cva('inline-flex items-center justify-center rounded-md text-xs font-medium whitespace-nowrap p-2 text-onVariant/90 hover:text-onSurface focus-within:ring-1 focus-within:ring-primary ml-1', {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why did you add the margin-left style to the chip component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unnecessary 👍 Removed it. Did add initially to give gap between chips

selected?: boolean;
condensed?: boolean;
value?: string;
highlighted?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the difference between selected and highlighted here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Selected - currently chosen (1st item)
Highlighted - currently focused via keyboard navigation (2nd item)

image


matchedValues = [...matchedValues, ...childValues];

return cloneElement(child, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed that cloneElement is used in several places in this file. What rational led you to make this choice?

};

// Handle keyboard navigation
const handleKeyDown = (e: React.KeyboardEvent) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you explain to me how this keyboard navigation is supposed to work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants