From 0a1580978c7a9565eb462ea53973bc24b758c6a6 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Mon, 1 Dec 2025 16:53:42 -0700 Subject: [PATCH 01/16] Fix Radix popovers not positioned correctly in certain DDK or Embedded scenarios --- .../src/components/css/datepickers.css | 7 ++- .../src/components/css/dropdown.css | 10 ++++ .../src/fragments/Dropdown.tsx | 46 +++++++++++++------ .../calendar/test_calendar_props.py | 1 + 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/components/dash-core-components/src/components/css/datepickers.css b/components/dash-core-components/src/components/css/datepickers.css index fdd608b043..2656ecbe2b 100644 --- a/components/dash-core-components/src/components/css/datepickers.css +++ b/components/dash-core-components/src/components/css/datepickers.css @@ -8,7 +8,7 @@ outline: none; width: 100%; font-size: inherit; - overflow: hidden; + position: relative; accent-color: var(--Dash-Fill-Interactive-Strong); outline-color: var(--Dash-Fill-Interactive-Strong); } @@ -231,3 +231,8 @@ width: 20px; height: 20px; } + +/* Override Radix's position: fixed to use position: absolute when using custom container */ +div[data-radix-popper-content-wrapper]:has(.dash-datepicker-content) { + position: absolute !important; +} diff --git a/components/dash-core-components/src/components/css/dropdown.css b/components/dash-core-components/src/components/css/dropdown.css index f4bddd7c88..5694f1b056 100644 --- a/components/dash-core-components/src/components/css/dropdown.css +++ b/components/dash-core-components/src/components/css/dropdown.css @@ -212,3 +212,13 @@ padding: calc(var(--Dash-Spacing) * 2) calc(var(--Dash-Spacing) * 3); box-shadow: 0 -1px 0 0 var(--Dash-Fill-Disabled) inset; } + +/* Positioning container for the dropdown */ +.dash-dropdown-wrapper { + position: relative; +} + +/* Override Radix's position: fixed to use position: absolute when using custom container */ +div[data-radix-popper-content-wrapper]:has(.dash-dropdown-content) { + position: absolute !important; +} diff --git a/components/dash-core-components/src/fragments/Dropdown.tsx b/components/dash-core-components/src/fragments/Dropdown.tsx index 88666713e2..8f393455b2 100644 --- a/components/dash-core-components/src/fragments/Dropdown.tsx +++ b/components/dash-core-components/src/fragments/Dropdown.tsx @@ -47,6 +47,7 @@ const Dropdown = (props: DropdownProps) => { const dropdownContentRef = useRef( document.createElement('div') ); + const searchInputRef = useRef(null); const ctx = window.dash_component_api.useDashContext(); const loading = ctx.useLoading(); @@ -234,22 +235,33 @@ const Dropdown = (props: DropdownProps) => { } }, [filteredOptions, isOpen]); - // Focus (and scroll) the first selected item when dropdown opens + // Focus first selected item or search input when dropdown opens useEffect(() => { - if (!isOpen || multi || search_value) { + if (!isOpen || search_value) { return; } // waiting for the DOM to be ready after the dropdown renders requestAnimationFrame(() => { - const selectedValue = sanitizedValues[0]; - - const selectedElement = dropdownContentRef.current.querySelector( - `.dash-options-list-option-checkbox[value="${selectedValue}"]` - ); + // Try to focus the first selected item (for single-select) + if (!multi) { + const selectedValue = sanitizedValues[0]; + if (selectedValue) { + const selectedElement = + dropdownContentRef.current.querySelector( + `.dash-options-list-option-checkbox[value="${selectedValue}"]` + ); + + if (selectedElement instanceof HTMLElement) { + selectedElement.focus(); + return; + } + } + } - if (selectedElement instanceof HTMLElement) { - selectedElement?.focus(); + // Fallback: focus search input if available and no selected item was focused + if (searchable && searchInputRef.current) { + searchInputRef.current.focus(); } }); }, [isOpen, multi, displayOptions, sanitizedValues]); @@ -335,7 +347,7 @@ const Dropdown = (props: DropdownProps) => { } else { focusableElements[nextIndex].scrollIntoView({ behavior: 'auto', - block: 'center', + block: 'nearest', }); } } @@ -354,8 +366,9 @@ const Dropdown = (props: DropdownProps) => { ); const accessibleId = id ?? uuid(); + const positioningContainerRef = useRef(null); - return ( + const popover = (