Skip to content

Commit cf1669f

Browse files
authored
Merge pull request #3516 from plotly/bugfix/dropdown-bugfixes
Bugfix: address dcc dropdown feedback
2 parents d475f28 + 2e9f9f0 commit cf1669f

File tree

8 files changed

+69
-44
lines changed

8 files changed

+69
-44
lines changed

components/dash-core-components/src/components/css/dropdown.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
box-sizing: border-box;
55
margin: calc(var(--Dash-Spacing) * 2) 0;
66
padding: 0;
7-
background: inherit;
7+
background: var(--Dash-Fill-Inverse-Strong);
88
border: none;
99
outline: none;
1010
width: 100%;
@@ -46,7 +46,7 @@
4646
}
4747

4848
.dash-dropdown:focus {
49-
outline: 1px solid var(--Dash-Fill-Interactive-Strong);
49+
border: 1px solid var(--Dash-Fill-Interactive-Strong);
5050
}
5151

5252
.dash-dropdown:disabled {
@@ -70,6 +70,10 @@
7070
display: inline;
7171
}
7272

73+
.dash-dropdown-value-item:not(:first-child)::before {
74+
content: ', ';
75+
}
76+
7377
.dash-dropdown-content {
7478
background: var(--Dash-Fill-Inverse-Strong);
7579
width: fit-content;

components/dash-core-components/src/components/css/input.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
}
1717

1818
.dash-input-container:focus-within {
19-
outline: 1px solid var(--Dash-Fill-Interactive-Strong);
19+
border: 1px solid var(--Dash-Fill-Interactive-Strong);
2020
}
2121

2222
.dash-input-container:has(.dash-input-element:disabled) {

components/dash-core-components/src/fragments/Dropdown.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,12 @@ const Dropdown = (props: DropdownProps) => {
156156
option => option.value === val
157157
);
158158
return (
159-
<React.Fragment key={`${option?.value}-${i}`}>
159+
<span
160+
key={`${option?.value}-${i}`}
161+
className="dash-dropdown-value-item"
162+
>
160163
{option && <OptionLabel {...option} index={i} />}
161-
{i === sanitizedValues.length - 1 ? '' : ', '}
162-
</React.Fragment>
164+
</span>
163165
);
164166
});
165167
return labels;
@@ -434,7 +436,9 @@ const Dropdown = (props: DropdownProps) => {
434436
onOpenAutoFocus={e => e.preventDefault()}
435437
onKeyDown={handleKeyDown}
436438
style={{
437-
maxHeight: maxHeight ? `${maxHeight}px` : 'auto',
439+
maxHeight: maxHeight
440+
? `min(${maxHeight}px, calc(100vh - 100px))`
441+
: 'calc(100vh - 100px)',
438442
}}
439443
>
440444
{searchable && (

components/dash-core-components/src/utils/optionRendering.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,28 @@ export const Option: React.FC<OptionProps> = ({
9292
aria-selected={isSelected}
9393
style={optionStyle}
9494
>
95-
<input
96-
type={inputType}
97-
checked={isSelected}
98-
name={id}
99-
value={
100-
typeof option.value === 'boolean'
101-
? `${option.value}`
102-
: option.value
103-
}
104-
disabled={!!option.disabled}
105-
onChange={() => onChange(option)}
106-
readOnly
107-
className={inputClassNames.join(' ')}
108-
style={inputStyle}
109-
/>
95+
<span className="dash-options-list-option-wrapper">
96+
<input
97+
type={inputType}
98+
checked={isSelected}
99+
name={id}
100+
value={
101+
typeof option.value === 'boolean'
102+
? `${option.value}`
103+
: option.value
104+
}
105+
disabled={!!option.disabled}
106+
onChange={() => onChange(option)}
107+
onKeyUp={e => {
108+
if (e.key === 'Enter' && inputType === 'checkbox') {
109+
onChange(option);
110+
}
111+
}}
112+
readOnly
113+
className={inputClassNames.join(' ')}
114+
style={inputStyle}
115+
/>
116+
</span>
110117
<span
111118
className={labelClassNames.join(' ')}
112119
style={labelStyle}

components/dash-core-components/tests/integration/dropdown/test_a11y.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,31 +99,42 @@ def send_keys(key):
9999
assert num_elements == 100
100100

101101
send_keys(1) # Expecting to be typing into the searh bar
102+
sleep(0.1) # Give time for the search to filter options
102103
num_elements = len(dash_duo.find_elements(".dash-dropdown-option"))
103104
assert num_elements == 19
104105

105106
send_keys(Keys.ARROW_DOWN) # Expecting to be navigating through the options
106107
send_keys(Keys.SPACE) # Expecting to be selecting
107-
assert dash_duo.find_element(".dash-dropdown-value").text == "1"
108+
value_items = dash_duo.find_elements(".dash-dropdown-value-item")
109+
assert len(value_items) == 1
110+
assert value_items[0].text == "1"
108111

109112
send_keys(Keys.ARROW_DOWN) # Expecting to be navigating through the options
110113
send_keys(Keys.SPACE) # Expecting to be selecting
111-
assert dash_duo.find_element(".dash-dropdown-value").text == "1, 10"
114+
value_items = dash_duo.find_elements(".dash-dropdown-value-item")
115+
assert len(value_items) == 2
116+
assert [item.text for item in value_items] == ["1", "10"]
112117

113118
send_keys(Keys.SPACE) # Expecting to be de-selecting
114-
assert dash_duo.find_element(".dash-dropdown-value").text == "1"
119+
value_items = dash_duo.find_elements(".dash-dropdown-value-item")
120+
assert len(value_items) == 1
121+
assert value_items[0].text == "1"
115122

116123
send_keys(Keys.ARROW_UP)
117124
send_keys(Keys.ARROW_UP)
118125
send_keys(Keys.ARROW_UP) # Expecting to wrap over to the last item
119126
send_keys(Keys.SPACE)
120-
assert dash_duo.find_element(".dash-dropdown-value").text == "1, 91"
127+
value_items = dash_duo.find_elements(".dash-dropdown-value-item")
128+
assert len(value_items) == 2
129+
assert [item.text for item in value_items] == ["1", "91"]
121130

122131
send_keys(
123132
Keys.ESCAPE
124133
) # Expecting focus to remain on the dropdown after escaping out
125134
sleep(0.25)
126-
assert dash_duo.find_element(".dash-dropdown-value").text == "1, 91"
135+
value_items = dash_duo.find_elements(".dash-dropdown-value-item")
136+
assert len(value_items) == 2
137+
assert [item.text for item in value_items] == ["1", "91"]
127138

128139
assert dash_duo.get_logs() == []
129140

components/dash-core-components/tests/integration/test_title_props.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def add_title_to_option(title):
5252
dash_dcc.start_server(app)
5353

5454
elements = [
55-
dash_dcc.wait_for_element("#dropdown_1 .dash-dropdown-value span"),
56-
dash_dcc.wait_for_element("#dropdown_2 .dash-dropdown-value span"),
55+
dash_dcc.wait_for_element("#dropdown_1 .dash-dropdown-value-item span"),
56+
dash_dcc.wait_for_element("#dropdown_2 .dash-dropdown-value-item span"),
5757
dash_dcc.wait_for_element("#checklist_1 .Select-value-label"),
5858
dash_dcc.wait_for_element("#radioitems_1 .Select-value-label"),
5959
]

tests/integration/renderer/test_children_reorder.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,16 @@ def swap_button_action(n_clicks, children):
6666
dash_duo.find_element(".dash-dropdown-option:nth-child(1)").click()
6767
dash_duo.wait_for_text_to_equal(f".dropdown_{i} .dash-dropdown-trigger", "A")
6868
dash_duo.find_element(".dash-dropdown-option:nth-child(2)").click()
69-
dash_duo.wait_for_text_to_equal(
70-
f".dropdown_{i} .dash-dropdown-trigger", "A, B\n2 selected"
71-
)
69+
value_items = dash_duo.find_elements(f".dropdown_{i} .dash-dropdown-value-item")
70+
assert [item.text for item in value_items] == ["A", "B"]
7271
dash_duo.find_element(".dash-dropdown-option:nth-child(3)").click()
73-
dash_duo.wait_for_text_to_equal(
74-
f".dropdown_{i} .dash-dropdown-trigger", "A, B, C\n3 selected"
75-
)
72+
value_items = dash_duo.find_elements(f".dropdown_{i} .dash-dropdown-value-item")
73+
assert [item.text for item in value_items] == ["A", "B", "C"]
74+
7675
dash_duo.find_element(f".swap_button_{i}").click()
77-
dash_duo.wait_for_text_to_equal(
78-
f".dropdown_{0} .dash-dropdown-trigger", "A, B, C\n3 selected"
79-
)
80-
dash_duo.wait_for_text_to_equal(
81-
f".dropdown_{1} .dash-dropdown-trigger", "A, B, C\n3 selected"
82-
)
76+
77+
value_items = dash_duo.find_elements(f".dropdown_{0} .dash-dropdown-value-item")
78+
assert [item.text for item in value_items] == ["A", "B", "C"]
79+
80+
value_items = dash_duo.find_elements(f".dropdown_{1} .dash-dropdown-value-item")
81+
assert [item.text for item in value_items] == ["A", "B", "C"]

tests/integration/renderer/test_component_as_prop.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,13 +393,13 @@ def opts(n):
393393

394394
dash_duo.wait_for_text_to_equal("#counter", "0")
395395
dash_duo.find_element("#a").click()
396-
assert len(dash_duo.find_elements("#b label > input")) == 2
396+
assert len(dash_duo.find_elements("#b label input")) == 2
397397
dash_duo.wait_for_text_to_equal("#counter", "0")
398398
dash_duo.find_element("#a").click()
399-
assert len(dash_duo.find_elements("#b label > input")) == 3
399+
assert len(dash_duo.find_elements("#b label input")) == 3
400400
dash_duo.wait_for_text_to_equal("#counter", "0")
401401

402-
dash_duo.find_elements("#b label > input")[0].click()
402+
dash_duo.find_elements("#b label input")[0].click()
403403
dash_duo.wait_for_text_to_equal("#counter", "1")
404404

405405

0 commit comments

Comments
 (0)