Skip to content

Commit 4b01ac8

Browse files
authored
Merge pull request #531 from facultyai/popover-triggers
Popover triggers
2 parents 3e2c8ee + 8e93960 commit 4b01ac8

File tree

5 files changed

+112
-25
lines changed

5 files changed

+112
-25
lines changed

docs/components_page/components/popover.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@ lead: Use the Popover component to add Bootstrap popovers to any component in yo
55

66
## Simple example
77

8-
To use `Popover`, add it to your layout, and set the `target` argument to the `id` of the component you would like to attach the popover to. Then define a callback that toggles the `is_open` property of the `Popover`. In the below example we use a simple callback to toggle the popover when a button is clicked. In this case the target of the popover is the button itself, but in general there is no requirement that the components used in the callback need to be related to the target.
8+
To use `Popover`, add it to your layout, and set the `target` argument to the `id` of the component you would like to attach the popover to. The easiest way to trigger the popover is to specify the `trigger` property. This should be a string containing any of the following 4 values (space separated)
99

10-
{{example:components/popover/popover.py:popover}}
10+
* `"click"`: toggles the popover when the target is clicked.
11+
* `"focus"`: toggles the popover when the target receives focus
12+
* `"hover"`: toggles the popover when the target is hovered over with the cursor.
13+
* `"legacy"`: toggles the popover when the target is clicked, but will also dismiss the popover when the user clicks outside of the popover.
14+
15+
{{example:components/popover/popover.py:popovers}}
16+
17+
## Toggling the Popover with callbacks
18+
19+
Alternatively, you can manually control the appearance of the popover using callbacks by setting the `is_open` property of the `Popover`. This can be useful for example when you want to make the popover appear after the user interacts with something other than the target component as demonstrated in the below example.
20+
21+
{{example:components/popover/popover_callback.py:popover}}
1122

1223
## Placement
1324

docs/components_page/components/popover/popover.py

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,46 @@
11
import dash_bootstrap_components as dbc
22
import dash_html_components as html
3-
from dash.dependencies import Input, Output, State
43

5-
popover = html.Div(
4+
popover_children = [
5+
dbc.PopoverHeader("Popover header"),
6+
dbc.PopoverBody("And here's some amazing content. Cool!"),
7+
]
8+
9+
popovers = html.Div(
610
[
711
dbc.Button(
8-
"Click to toggle popover", id="popover-target", color="danger"
12+
"Click", id="click-target", color="danger", className="mr-1"
13+
),
14+
dbc.Popover(
15+
popover_children,
16+
id="click",
17+
target="click-target",
18+
trigger="click",
19+
),
20+
dbc.Button(
21+
"Focus", id="focus-target", color="danger", className="mr-1"
22+
),
23+
dbc.Popover(
24+
popover_children,
25+
id="focus",
26+
target="focus-target",
27+
trigger="focus",
28+
),
29+
dbc.Button(
30+
"Hover", id="hover-target", color="danger", className="mr-1"
31+
),
32+
dbc.Popover(
33+
popover_children,
34+
id="hover",
35+
target="hover-target",
36+
trigger="hover",
937
),
38+
dbc.Button("Legacy", id="legacy-target", color="danger"),
1039
dbc.Popover(
11-
[
12-
dbc.PopoverHeader("Popover header"),
13-
dbc.PopoverBody("And here's some amazing content. Cool!"),
14-
],
15-
id="popover",
16-
is_open=False,
17-
target="popover-target",
40+
popover_children,
41+
id="legacy",
42+
target="legacy-target",
43+
trigger="legacy",
1844
),
1945
]
2046
)
21-
22-
23-
@app.callback(
24-
Output("popover", "is_open"),
25-
[Input("popover-target", "n_clicks")],
26-
[State("popover", "is_open")],
27-
)
28-
def toggle_popover(n, is_open):
29-
if n:
30-
return not is_open
31-
return is_open
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import dash_bootstrap_components as dbc
2+
import dash_html_components as html
3+
from dash.dependencies import Input, Output, State
4+
5+
popover = html.Div(
6+
[
7+
dbc.Button("Toggle", id="toggle", color="success", className="mr-4"),
8+
dbc.Button("Target", id="target", color="danger"),
9+
dbc.Popover(
10+
[
11+
dbc.PopoverHeader("Popover header"),
12+
dbc.PopoverBody("And here's some amazing content. Cool!"),
13+
],
14+
id="popover",
15+
is_open=False,
16+
target="target",
17+
),
18+
]
19+
)
20+
21+
22+
@app.callback(
23+
Output("popover", "is_open"),
24+
[Input("toggle", "n_clicks")],
25+
[State("popover", "is_open")],
26+
)
27+
def toggle_popover(n, is_open):
28+
if n:
29+
return not is_open
30+
return is_open

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ exclude =
2727
docs/components_page/components/nav/navlink.py,
2828
docs/components_page/components/navbar/navbar.py,
2929
docs/components_page/components/popover/direction.py,
30-
docs/components_page/components/popover/popover.py,
30+
docs/components_page/components/popover/popover_callback.py,
3131
docs/components_page/components/progress/animated.py,
3232
docs/components_page/components/progress/interval.py,
3333
docs/components_page/components/spinner/loading.py,

src/components/popover/Popover.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,28 @@ import {Popover as RSPopover} from 'reactstrap';
1212
* of the children.
1313
*/
1414
const Popover = props => {
15-
const {children, is_open, hide_arrow, loading_state, ...otherProps} = props;
15+
const {
16+
children,
17+
is_open,
18+
hide_arrow,
19+
loading_state,
20+
setProps,
21+
trigger,
22+
...otherProps
23+
} = props;
24+
25+
const toggle = () => {
26+
setProps({is_open: !is_open});
27+
};
28+
1629
return (
1730
<RSPopover
1831
isOpen={is_open}
1932
hideArrow={hide_arrow}
33+
// to ensure proper backwards compatibility, the toggle function is only
34+
// passed to the popover if `trigger` is not specified
35+
toggle={trigger ? toggle : null}
36+
trigger={trigger}
2037
{...omit(['setProps'], otherProps)}
2138
data-dash-is-loading={
2239
(loading_state && loading_state.is_loading) || undefined
@@ -87,6 +104,20 @@ Popover.propTypes = {
87104
*/
88105
container: PropTypes.string,
89106

107+
/**
108+
* Space separated list of triggers (e.g. "click hover focus legacy"). These
109+
* specify ways in which the target component can toggle the popover. If not
110+
* specified you must toggle the popover yourself using callbacks. Options
111+
* are:
112+
* - "click": toggles the popover when the target is clicked.
113+
* - "hover": toggles the popover when the target is hovered over with the
114+
* cursor.
115+
* - "focus": toggles the popover when the target receives focus
116+
* - "legacy": toggles the popover when the target is clicked, but will also
117+
* dismiss the popover when the user clicks outside of the popover.
118+
*/
119+
trigger: PropTypes.string,
120+
90121
/**
91122
* Whether the Popover is open or not.
92123
*/

0 commit comments

Comments
 (0)