Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Expose Dropdown option 'title' property #793

Merged
merged 12 commits into from
Apr 30, 2020
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
## [Unreleased] -
### Changed
- [#793](https://github.com/plotly/dash-core-components/pull/793) Added title key (i.e. HTML `title` attribute) to option dicts in `dcc.Dropdown` `options[]` list property.
### Fixed
- [#790](https://github.com/plotly/dash-core-components/pull/790) Fixed bug where the dcc.Dropdown dropdown was hidden by the dash_table.DataTable fixed rows and columns.

Expand Down
7 changes: 7 additions & 0 deletions src/components/Dropdown.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ Dropdown.propTypes = {
* If true, this option is disabled and cannot be selected.
*/
disabled: PropTypes.bool,

/**
* The HTML 'title' attribute for the option. Allows for
* information on hover. For more information on this attribute,
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title
*/
title: PropTypes.string,
})
),

Expand Down
91 changes: 91 additions & 0 deletions tests/integration/dropdown/test_option_title_prop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: UTF-8 -*-

import pytest
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html

from utils import wait_for


@pytest.mark.DCC793
@pytest.mark.parametrize("multi", [True, False])
def test_ddot001_option_title(dash_dcc, multi):
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(
id="dropdown_title_input",
type="text",
placeholder="Enter a title for New York City"
),
dcc.Dropdown(
id="dropdown",
options=[
{'label': 'New York City', 'value': 'NYC'},
{'label': 'Montréal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
],
value='NYC',
multi=multi
)
])

@app.callback(Output("dropdown", "options"), [Input("dropdown_title_input", "value")])
def add_title_to_option(title):
return [
{'label': 'New York City', 'title': title, 'value': 'NYC'},
{'label': 'Montréal', 'value': 'MTL'},
{'label': 'San Francisco', 'value': 'SF'}
]

dash_dcc.start_server(app)

dropdown_option_element = dash_dcc.wait_for_element("#dropdown .Select-value")

dropdown_title_input = dash_dcc.wait_for_element("#dropdown_title_input")

# Empty string title ('') (default for no title)
wait_for(
lambda: dropdown_option_element.get_attribute("title")
== '',
lambda: '`title` is {}, expected {}'.format(
dropdown_option_element.get_attribute("title"),
'',
),
)

dropdown_title_input.send_keys("The Big Apple")

wait_for(
lambda: dropdown_option_element.get_attribute("title")
== "The Big Apple",
lambda: '`title` is {}, expected {}'.format(
dropdown_option_element.get_attribute("title"),
"The Big Apple",
),
)
Copy link
Contributor

@Marc-Andre-Rivet Marc-Andre-Rivet Apr 15, 2020

Choose a reason for hiding this comment

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

@wbrgss I'm not sure why there's a callback to update the title on the 1st option: If all we want to do is make sure it's present, we could set it in the initial layout. If we want to make sure it's present after an update, we should change it more than once and make sure it changes None --> "Big Apple" --> "Knicks Game" --> None for example.

I'm ok with us not testing further (eg. title present in the dropdown, not just the selected value) as that would be the same as testing the 3rd party component that we're wrapping.


Using wait_for_element instead of find_element_by_css_selector makes for a slightly more verbose test but also for a slightly more resilient test (eg. timing issues in CI). That said the test app is pretty small and probably will rarely be an issue. Same goes for wait_for vs. simply getting the attribute. This is 👌

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we want to make sure it's present after an update, we should change it more than once and make sure it changes None --> "Big Apple" --> "Knicks Game" --> None for example.

Agreed, that makes sense. I'll update it.

makes for a slightly more verbose test but also for a slightly more resilient test

Yeah, I've had timing issues here and there with more complicated test apps so I've been using wait_for for overkill to be safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we want to make sure it's present after an update, we should change it more than once and make sure it changes None --> "Big Apple" --> "Knicks Game" --> None for example.

@Marc-Andre-Rivet I've done this in d5bee5d; if that looks right to you I'm going to merge.


dash_dcc.clear_input(dropdown_title_input)

dropdown_title_input.send_keys("Gotham City?")

wait_for(
lambda: dropdown_option_element.get_attribute("title")
== "Gotham City?",
lambda: '`title` is {}, expected {}'.format(
dropdown_option_element.get_attribute("title"),
"Gotham City?",
),
)

dash_dcc.clear_input(dropdown_title_input)

wait_for(
lambda: dropdown_option_element.get_attribute("title")
== '',
lambda: '`title` is {}, expected {}'.format(
dropdown_option_element.get_attribute("title"),
'',
),
)