From 7891aa49cc41165687bddde86bf9f8516d4fa4e6 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Tue, 18 Nov 2025 11:22:41 -0700 Subject: [PATCH 1/5] Remove hard-coded commas from selected value --- .../src/components/css/dropdown.css | 4 ++++ .../src/fragments/Dropdown.tsx | 8 ++++--- .../tests/integration/dropdown/test_a11y.py | 21 ++++++++++++++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/components/dash-core-components/src/components/css/dropdown.css b/components/dash-core-components/src/components/css/dropdown.css index 7746c49a75..4e9b7b13e1 100644 --- a/components/dash-core-components/src/components/css/dropdown.css +++ b/components/dash-core-components/src/components/css/dropdown.css @@ -70,6 +70,10 @@ display: inline; } +.dash-dropdown-value-item:not(:first-child)::before { + content: ', '; +} + .dash-dropdown-content { background: var(--Dash-Fill-Inverse-Strong); width: fit-content; diff --git a/components/dash-core-components/src/fragments/Dropdown.tsx b/components/dash-core-components/src/fragments/Dropdown.tsx index 6808ff7a84..ccdea485b2 100644 --- a/components/dash-core-components/src/fragments/Dropdown.tsx +++ b/components/dash-core-components/src/fragments/Dropdown.tsx @@ -156,10 +156,12 @@ const Dropdown = (props: DropdownProps) => { option => option.value === val ); return ( - + {option && } - {i === sanitizedValues.length - 1 ? '' : ', '} - + ); }); return labels; diff --git a/components/dash-core-components/tests/integration/dropdown/test_a11y.py b/components/dash-core-components/tests/integration/dropdown/test_a11y.py index 85e859bbbb..8f937afea8 100644 --- a/components/dash-core-components/tests/integration/dropdown/test_a11y.py +++ b/components/dash-core-components/tests/integration/dropdown/test_a11y.py @@ -99,31 +99,42 @@ def send_keys(key): assert num_elements == 100 send_keys(1) # Expecting to be typing into the searh bar + sleep(0.1) # Give time for the search to filter options num_elements = len(dash_duo.find_elements(".dash-dropdown-option")) assert num_elements == 19 send_keys(Keys.ARROW_DOWN) # Expecting to be navigating through the options send_keys(Keys.SPACE) # Expecting to be selecting - assert dash_duo.find_element(".dash-dropdown-value").text == "1" + value_items = dash_duo.find_elements(".dash-dropdown-value-item") + assert len(value_items) == 1 + assert value_items[0].text == "1" send_keys(Keys.ARROW_DOWN) # Expecting to be navigating through the options send_keys(Keys.SPACE) # Expecting to be selecting - assert dash_duo.find_element(".dash-dropdown-value").text == "1, 10" + value_items = dash_duo.find_elements(".dash-dropdown-value-item") + assert len(value_items) == 2 + assert [item.text for item in value_items] == ["1", "10"] send_keys(Keys.SPACE) # Expecting to be de-selecting - assert dash_duo.find_element(".dash-dropdown-value").text == "1" + value_items = dash_duo.find_elements(".dash-dropdown-value-item") + assert len(value_items) == 1 + assert value_items[0].text == "1" send_keys(Keys.ARROW_UP) send_keys(Keys.ARROW_UP) send_keys(Keys.ARROW_UP) # Expecting to wrap over to the last item send_keys(Keys.SPACE) - assert dash_duo.find_element(".dash-dropdown-value").text == "1, 91" + value_items = dash_duo.find_elements(".dash-dropdown-value-item") + assert len(value_items) == 2 + assert [item.text for item in value_items] == ["1", "91"] send_keys( Keys.ESCAPE ) # Expecting focus to remain on the dropdown after escaping out sleep(0.25) - assert dash_duo.find_element(".dash-dropdown-value").text == "1, 91" + value_items = dash_duo.find_elements(".dash-dropdown-value-item") + assert len(value_items) == 2 + assert [item.text for item in value_items] == ["1", "91"] assert dash_duo.get_logs() == [] From ca654e4a413a9c78b5fa031d6bbc38a0f9f8526a Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Tue, 18 Nov 2025 11:45:41 -0700 Subject: [PATCH 2/5] prevent dropdown height from exceeding viewport height --- components/dash-core-components/src/fragments/Dropdown.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/dash-core-components/src/fragments/Dropdown.tsx b/components/dash-core-components/src/fragments/Dropdown.tsx index ccdea485b2..88666713e2 100644 --- a/components/dash-core-components/src/fragments/Dropdown.tsx +++ b/components/dash-core-components/src/fragments/Dropdown.tsx @@ -436,7 +436,9 @@ const Dropdown = (props: DropdownProps) => { onOpenAutoFocus={e => e.preventDefault()} onKeyDown={handleKeyDown} style={{ - maxHeight: maxHeight ? `${maxHeight}px` : 'auto', + maxHeight: maxHeight + ? `min(${maxHeight}px, calc(100vh - 100px))` + : 'calc(100vh - 100px)', }} > {searchable && ( From baff0466e5267e90053848c5cf7c57f66084281a Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Tue, 18 Nov 2025 11:46:11 -0700 Subject: [PATCH 3/5] wrap checkboxes in a and make them respond to Enter key --- .../src/utils/optionRendering.tsx | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/components/dash-core-components/src/utils/optionRendering.tsx b/components/dash-core-components/src/utils/optionRendering.tsx index 10b62cec84..40ffcacfc7 100644 --- a/components/dash-core-components/src/utils/optionRendering.tsx +++ b/components/dash-core-components/src/utils/optionRendering.tsx @@ -92,21 +92,28 @@ export const Option: React.FC = ({ aria-selected={isSelected} style={optionStyle} > - onChange(option)} - readOnly - className={inputClassNames.join(' ')} - style={inputStyle} - /> + + onChange(option)} + onKeyUp={e => { + if (e.key === 'Enter' && inputType === 'checkbox') { + onChange(option); + } + }} + readOnly + className={inputClassNames.join(' ')} + style={inputStyle} + /> + Date: Thu, 20 Nov 2025 14:19:35 -0700 Subject: [PATCH 4/5] fix test --- .../tests/integration/test_title_props.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/dash-core-components/tests/integration/test_title_props.py b/components/dash-core-components/tests/integration/test_title_props.py index a86bac7112..ef1246c9d8 100644 --- a/components/dash-core-components/tests/integration/test_title_props.py +++ b/components/dash-core-components/tests/integration/test_title_props.py @@ -52,8 +52,8 @@ def add_title_to_option(title): dash_dcc.start_server(app) elements = [ - dash_dcc.wait_for_element("#dropdown_1 .dash-dropdown-value span"), - dash_dcc.wait_for_element("#dropdown_2 .dash-dropdown-value span"), + dash_dcc.wait_for_element("#dropdown_1 .dash-dropdown-value-item span"), + dash_dcc.wait_for_element("#dropdown_2 .dash-dropdown-value-item span"), dash_dcc.wait_for_element("#checklist_1 .Select-value-label"), dash_dcc.wait_for_element("#radioitems_1 .Select-value-label"), ] From f9f6763f05540e2e38e06d5f9f04f09d1a4152df Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Thu, 20 Nov 2025 16:24:57 -0700 Subject: [PATCH 5/5] fix tests --- .../renderer/test_children_reorder.py | 23 +++++++++---------- .../renderer/test_component_as_prop.py | 6 ++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/integration/renderer/test_children_reorder.py b/tests/integration/renderer/test_children_reorder.py index e4d07ee541..11c1f0e660 100644 --- a/tests/integration/renderer/test_children_reorder.py +++ b/tests/integration/renderer/test_children_reorder.py @@ -66,17 +66,16 @@ def swap_button_action(n_clicks, children): dash_duo.find_element(".dash-dropdown-option:nth-child(1)").click() dash_duo.wait_for_text_to_equal(f".dropdown_{i} .dash-dropdown-trigger", "A") dash_duo.find_element(".dash-dropdown-option:nth-child(2)").click() - dash_duo.wait_for_text_to_equal( - f".dropdown_{i} .dash-dropdown-trigger", "A, B\n2 selected" - ) + value_items = dash_duo.find_elements(f".dropdown_{i} .dash-dropdown-value-item") + assert [item.text for item in value_items] == ["A", "B"] dash_duo.find_element(".dash-dropdown-option:nth-child(3)").click() - dash_duo.wait_for_text_to_equal( - f".dropdown_{i} .dash-dropdown-trigger", "A, B, C\n3 selected" - ) + value_items = dash_duo.find_elements(f".dropdown_{i} .dash-dropdown-value-item") + assert [item.text for item in value_items] == ["A", "B", "C"] + dash_duo.find_element(f".swap_button_{i}").click() - dash_duo.wait_for_text_to_equal( - f".dropdown_{0} .dash-dropdown-trigger", "A, B, C\n3 selected" - ) - dash_duo.wait_for_text_to_equal( - f".dropdown_{1} .dash-dropdown-trigger", "A, B, C\n3 selected" - ) + + value_items = dash_duo.find_elements(f".dropdown_{0} .dash-dropdown-value-item") + assert [item.text for item in value_items] == ["A", "B", "C"] + + value_items = dash_duo.find_elements(f".dropdown_{1} .dash-dropdown-value-item") + assert [item.text for item in value_items] == ["A", "B", "C"] diff --git a/tests/integration/renderer/test_component_as_prop.py b/tests/integration/renderer/test_component_as_prop.py index f272d02d7c..8c620977d5 100644 --- a/tests/integration/renderer/test_component_as_prop.py +++ b/tests/integration/renderer/test_component_as_prop.py @@ -393,13 +393,13 @@ def opts(n): dash_duo.wait_for_text_to_equal("#counter", "0") dash_duo.find_element("#a").click() - assert len(dash_duo.find_elements("#b label > input")) == 2 + assert len(dash_duo.find_elements("#b label input")) == 2 dash_duo.wait_for_text_to_equal("#counter", "0") dash_duo.find_element("#a").click() - assert len(dash_duo.find_elements("#b label > input")) == 3 + assert len(dash_duo.find_elements("#b label input")) == 3 dash_duo.wait_for_text_to_equal("#counter", "0") - dash_duo.find_elements("#b label > input")[0].click() + dash_duo.find_elements("#b label input")[0].click() dash_duo.wait_for_text_to_equal("#counter", "1")