From dc5b7bd298bb53db69b1f38ffa860dac592ab59a Mon Sep 17 00:00:00 2001 From: Luis Ibarra Date: Wed, 25 Mar 2026 09:37:49 -0500 Subject: [PATCH 1/5] feat: add dropdown list item height constant and scrollbar styles - Introduced a new constant `SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX` for consistent row height in dropdowns. - Updated multiple components to utilize the new constant for list item height. - Added custom scrollbar styles for virtual lists to enhance UI consistency across select components. This change improves the visual consistency of dropdowns and enhances user experience by ensuring proper scrollbar visibility. --- app/client/src/components/constants.ts | 7 +++ .../component/index.styled.tsx | 6 ++- .../MultiSelectTreeWidget/component/index.tsx | 6 ++- .../component/index.styled.tsx | 2 + .../MultiSelectWidget/component/index.tsx | 7 ++- .../component/index.styled.tsx | 45 +++++++++++++++++++ .../MultiSelectWidgetV2/component/index.tsx | 6 ++- .../component/index.styled.tsx | 6 ++- .../component/index.tsx | 6 ++- 9 files changed, 85 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index ec6d1269f171..fb6012da76ec 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -107,6 +107,13 @@ export enum LabelPosition { */ export const SELECT_DEFAULT_HEIGHT = "32px"; +/** + * Row height passed to rc-select / rc-tree-select virtual lists. Must match dropdown + * option CSS (.rc-select-item, tree treenode). If smaller than real rows, the list + * thinks content fits, omits the scrollbar, and overflow stays hidden. + */ +export const SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX = 38; + /** * Default margin bottom value for old select widgets */ diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx index 2b7b3d990175..13cb7f2e740a 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx @@ -7,7 +7,10 @@ import { LABEL_MARGIN_OLD_SELECT, SELECT_DEFAULT_HEIGHT, } from "components/constants"; -import { CommonSelectFilterStyle } from "widgets/MultiSelectWidgetV2/component/index.styled"; +import { + CommonSelectFilterStyle, + RcVirtualListScrollbarStyles, +} from "widgets/MultiSelectWidgetV2/component/index.styled"; import { Icon, labelLayoutStyles, @@ -277,6 +280,7 @@ border: 1px solid #E8E8E8; outline: none !important; } ${CommonSelectFilterStyle} + ${RcVirtualListScrollbarStyles} .rc-tree-select-item { font-size: 16px; line-height: 1.5; diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx index e5d902c8a651..12e24f51f307 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx @@ -26,7 +26,10 @@ import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Icon } from "@design-system/widgets-old"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import useDropdown from "widgets/useDropdown"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; @@ -292,6 +295,7 @@ function MultiTreeSelectComponent({ /> } key={key} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx index 063a993d0404..4046b8a62f86 100644 --- a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx @@ -4,6 +4,7 @@ import styled, { keyframes, createGlobalStyle } from "styled-components"; import { Colors } from "constants/Colors"; import type { LabelPosition } from "components/constants"; import { labelLayoutStyles } from "@design-system/widgets-old"; +import { RcVirtualListScrollbarStyles } from "widgets/MultiSelectWidgetV2/component/index.styled"; const rcSelectDropdownSlideUpIn = keyframes` 0% { @@ -222,6 +223,7 @@ ${({ dropDownWidth, id, parentWidth }) => ` margin-bottom: 0; } } + ${RcVirtualListScrollbarStyles} } `; diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.tsx index f98604555613..39730468322c 100644 --- a/app/client/src/widgets/MultiSelectWidget/component/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/component/index.tsx @@ -20,7 +20,10 @@ import { Classes } from "@blueprintjs/core"; import { WidgetContainerDiff } from "widgets/WidgetUtils"; import _ from "lodash"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; const menuItemSelectedIcon = (props: { isSelected: boolean }) => { @@ -225,6 +228,8 @@ function MultiSelectComponent({ name="dropdown" /> } + listHeight={300} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index 674850f87970..23b84a659ce2 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -113,6 +113,50 @@ export const CommonSelectFilterStyle = css<{ } `; +/** + * rc-virtual-list uses a custom thumb (not native scrollbars). Match Simple SelectWidget: + * white dropdown (select-popover-wrapper) + assets/styles/index.css (5px thumb, + * ads-v2 emphasis, 36px radius, thumb opacity 0.5 on :hover for webkit). + */ +export const RcVirtualListScrollbarStyles = css` + .rc-virtual-list-scrollbar-vertical, + .rc-tree-select-tree-list-scrollbar-vertical { + visibility: visible !important; + width: 10px !important; + min-width: 10px !important; + box-sizing: border-box !important; + background-color: #fff !important; + border-inline-start: 1px solid var(--wds-color-border); + } + .rc-virtual-list-scrollbar-horizontal, + .rc-tree-select-tree-list-scrollbar-horizontal { + visibility: visible !important; + height: 8px !important; + min-height: 8px !important; + box-sizing: border-box !important; + background-color: #fff !important; + border-block-start: 1px solid var(--wds-color-border); + } + .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar + .rc-tree-select-tree-list-scrollbar-thumb { + background-color: var( + --ads-v2-color-bg-emphasis, + rgba(0, 0, 0, 0.35) + ) !important; + border-radius: 36px !important; + opacity: 0.5; + } + /* 5px thumb centered in the white gutter (globals use 5px ::-webkit-scrollbar width) */ + .rc-virtual-list-scrollbar-vertical .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar-vertical + .rc-tree-select-tree-list-scrollbar-thumb { + width: 5px !important; + inset-inline-start: auto !important; + inset-inline-end: 2px !important; + } +`; + const Indicator = styled.div` width: 1.2em; height: 1.2em; @@ -381,6 +425,7 @@ ${({ dropDownWidth, id }) => ` margin-bottom: 0; } } + ${RcVirtualListScrollbarStyles} } `; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx index f29f6d1107bb..d62defc7015c 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx @@ -22,7 +22,10 @@ import type { Alignment } from "@blueprintjs/core"; import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import { uniqBy } from "lodash"; import { Icon } from "@design-system/widgets-old"; import useDropdown from "widgets/useDropdown"; @@ -361,6 +364,7 @@ function MultiSelectComponent({ } labelInValue listHeight={300} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx index d318e4c0d1d5..968f177ea0c0 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx @@ -8,7 +8,10 @@ import { LABEL_MARGIN_OLD_SELECT, SELECT_DEFAULT_HEIGHT, } from "components/constants"; -import { CommonSelectFilterStyle } from "widgets/MultiSelectWidgetV2/component/index.styled"; +import { + CommonSelectFilterStyle, + RcVirtualListScrollbarStyles, +} from "widgets/MultiSelectWidgetV2/component/index.styled"; import { Icon, labelLayoutStyles, @@ -243,6 +246,7 @@ ${({ dropDownWidth, id }) => ` outline: none !important; } ${CommonSelectFilterStyle} + ${RcVirtualListScrollbarStyles} .rc-tree-select-item { font-size: 16px; line-height: 1.5; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx index 57158beac6e1..012f06a0e7ca 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx @@ -25,7 +25,10 @@ import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Icon } from "@design-system/widgets-old"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import useDropdown from "widgets/useDropdown"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; import { isNil } from "lodash"; @@ -304,6 +307,7 @@ function SingleSelectTreeComponent({ /> } key={key} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} From 064c7e758bc17b741cc95b47c0bf0ccca4b3b265 Mon Sep 17 00:00:00 2001 From: Stacey Levine Date: Wed, 25 Mar 2026 11:48:44 -0400 Subject: [PATCH 2/5] fix: reserve space for rc-virtual-list scrollbar to prevent transparency Items were extending full-width under the absolutely-positioned custom scrollbar, causing row backgrounds to bleed through the track. Add padding-right to holder-inner so items stop short of the scrollbar. Co-Authored-By: Claude Opus 4.6 --- .../widgets/MultiSelectWidgetV2/component/index.styled.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index 23b84a659ce2..bc6f94b69a3a 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -119,6 +119,11 @@ export const CommonSelectFilterStyle = css<{ * ads-v2 emphasis, 36px radius, thumb opacity 0.5 on :hover for webkit). */ export const RcVirtualListScrollbarStyles = css` + /* Reserve space so items don't render under the absolutely-positioned scrollbar */ + .rc-virtual-list-holder-inner, + .rc-tree-select-tree-list-holder-inner { + padding-right: 10px; + } .rc-virtual-list-scrollbar-vertical, .rc-tree-select-tree-list-scrollbar-vertical { visibility: visible !important; From 754961fd2e771e238364b1de70794fcbd91a0910 Mon Sep 17 00:00:00 2001 From: Luis Ibarra Date: Wed, 25 Mar 2026 14:47:19 -0500 Subject: [PATCH 3/5] refactor: enhance rc-virtual-list scrollbar styles for improved UI consistency - Introduced a constant for scrollbar thumb dimensions to standardize styling. - Refactored scrollbar styles to utilize a base style for both vertical and horizontal scrollbars, ensuring consistent appearance across components. - Improved visibility and aesthetics of custom scrollbars in the rc-virtual-list and tree-select components. This change enhances the user experience by providing a more cohesive look and feel for dropdowns and selection widgets. --- .../component/index.styled.tsx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index bc6f94b69a3a..e43ac9f52d7d 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -113,34 +113,35 @@ export const CommonSelectFilterStyle = css<{ } `; +/** Virtual-list / tree-select thumb width and height (horizontal track is 8px tall). */ +const RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX = 6; + +const rcVirtualListScrollbarTrackBase = css` + visibility: visible !important; + box-sizing: border-box !important; + background-color: #fff !important; +`; + /** - * rc-virtual-list uses a custom thumb (not native scrollbars). Match Simple SelectWidget: - * white dropdown (select-popover-wrapper) + assets/styles/index.css (5px thumb, - * ads-v2 emphasis, 36px radius, thumb opacity 0.5 on :hover for webkit). + * rc-virtual-list uses a custom thumb (not native scrollbars). White track gutter; + * thumb uses --ads-v2-color-bg-emphasis and 36px radius (aligned with SelectWidget globals). */ export const RcVirtualListScrollbarStyles = css` - /* Reserve space so items don't render under the absolutely-positioned scrollbar */ .rc-virtual-list-holder-inner, .rc-tree-select-tree-list-holder-inner { padding-right: 10px; } .rc-virtual-list-scrollbar-vertical, .rc-tree-select-tree-list-scrollbar-vertical { - visibility: visible !important; + ${rcVirtualListScrollbarTrackBase} width: 10px !important; min-width: 10px !important; - box-sizing: border-box !important; - background-color: #fff !important; - border-inline-start: 1px solid var(--wds-color-border); } .rc-virtual-list-scrollbar-horizontal, .rc-tree-select-tree-list-scrollbar-horizontal { - visibility: visible !important; + ${rcVirtualListScrollbarTrackBase} height: 8px !important; min-height: 8px !important; - box-sizing: border-box !important; - background-color: #fff !important; - border-block-start: 1px solid var(--wds-color-border); } .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb, .rc-tree-select-tree-list-scrollbar @@ -150,16 +151,19 @@ export const RcVirtualListScrollbarStyles = css` rgba(0, 0, 0, 0.35) ) !important; border-radius: 36px !important; - opacity: 0.5; } - /* 5px thumb centered in the white gutter (globals use 5px ::-webkit-scrollbar width) */ .rc-virtual-list-scrollbar-vertical .rc-virtual-list-scrollbar-thumb, .rc-tree-select-tree-list-scrollbar-vertical .rc-tree-select-tree-list-scrollbar-thumb { - width: 5px !important; + width: ${RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX}px !important; inset-inline-start: auto !important; inset-inline-end: 2px !important; } + .rc-virtual-list-scrollbar-horizontal .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar-horizontal + .rc-tree-select-tree-list-scrollbar-thumb { + height: ${RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX}px !important; + } `; const Indicator = styled.div` From 11c959d63fb00a381afded2d5f4a03a9682c946f Mon Sep 17 00:00:00 2001 From: Luis Ibarra Date: Wed, 25 Mar 2026 15:14:00 -0500 Subject: [PATCH 4/5] style: update scrollbar padding for improved layout consistency Changed `padding-right` to `padding-inline-end` in the scrollbar styles for the rc-virtual-list and tree-select components to enhance layout consistency across different screen sizes and improve overall UI appearance. --- .../src/widgets/MultiSelectWidgetV2/component/index.styled.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index e43ac9f52d7d..fc3b04b8e08d 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -129,7 +129,7 @@ const rcVirtualListScrollbarTrackBase = css` export const RcVirtualListScrollbarStyles = css` .rc-virtual-list-holder-inner, .rc-tree-select-tree-list-holder-inner { - padding-right: 10px; + padding-inline-end: 10px; } .rc-virtual-list-scrollbar-vertical, .rc-tree-select-tree-list-scrollbar-vertical { From 4f5946e6c9231427a89b60d5e22970701a96c3b9 Mon Sep 17 00:00:00 2001 From: Luis Ibarra Date: Fri, 27 Mar 2026 13:45:54 -0500 Subject: [PATCH 5/5] style: refine scrollbar styles for rc-virtual-list and tree-select components Updated scrollbar dimensions and padding to enhance layout consistency and visual appeal. Introduced a constant for gutter width to ensure proper spacing when vertical scrollbars are present, improving overall user experience across selection widgets. --- .../component/index.styled.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index fc3b04b8e08d..ff376b28df92 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -113,29 +113,36 @@ export const CommonSelectFilterStyle = css<{ } `; -/** Virtual-list / tree-select thumb width and height (horizontal track is 8px tall). */ +/** Thumb size (vertical width / horizontal height). Horizontal track strip is 8px tall. */ const RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX = 6; +/** Matches vertical track width; list padding uses :has() so short lists don’t reserve a gutter. */ +const RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX = 10; const rcVirtualListScrollbarTrackBase = css` - visibility: visible !important; box-sizing: border-box !important; background-color: #fff !important; `; /** - * rc-virtual-list uses a custom thumb (not native scrollbars). White track gutter; - * thumb uses --ads-v2-color-bg-emphasis and 36px radius (aligned with SelectWidget globals). + * rc-virtual-list / rc-tree-select custom scrollbars: white track, emphasis thumb, 36px radius. + * Padding and gutter width stay in sync when a vertical scrollbar is mounted (`:has(...)`). */ export const RcVirtualListScrollbarStyles = css` .rc-virtual-list-holder-inner, .rc-tree-select-tree-list-holder-inner { - padding-inline-end: 10px; + padding-inline-end: 0; + } + .rc-virtual-list:has(.rc-virtual-list-scrollbar-vertical) + .rc-virtual-list-holder-inner, + .rc-tree-select-tree-list:has(.rc-tree-select-tree-list-scrollbar-vertical) + .rc-tree-select-tree-list-holder-inner { + padding-inline-end: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px; } .rc-virtual-list-scrollbar-vertical, .rc-tree-select-tree-list-scrollbar-vertical { ${rcVirtualListScrollbarTrackBase} - width: 10px !important; - min-width: 10px !important; + width: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px !important; + min-width: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px !important; } .rc-virtual-list-scrollbar-horizontal, .rc-tree-select-tree-list-scrollbar-horizontal {