Skip to content

Commit 2c75623

Browse files
authored
Merge pull request dbc-team#134 from facultyai/checkbox-radio-components
Add Checkbox and RadioButton components
2 parents f4db233 + 375baed commit 2c75623

File tree

8 files changed

+285
-26
lines changed

8 files changed

+285
-26
lines changed

docs/components_page/components/input/__init__.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
load_source_with_environment,
1111
)
1212
from ...metadata import get_component_metadata
13-
from .radio_check import inputs as input_radio_check
1413
from .size import inputs as input_size
1514
from .text_label import text_input as input_text_label
1615
from .textarea import textareas as input_textarea
1716
from .validation import inputs as input_validation
17+
from .radio_check_inline import inline_inputs
1818

1919
HERE = Path(__file__).parent
2020

@@ -24,6 +24,10 @@
2424
input_validation_source = (HERE / "validation.py").read_text()
2525
input_radio_check_source = (HERE / "radio_check.py").read_text()
2626
input_textarea_source = (HERE / "textarea.py").read_text()
27+
input_radio_check_inline_source = (HERE / "radio_check_inline.py").read_text()
28+
input_radio_check_standalone_source = (
29+
HERE / "radio_check_standalone.py"
30+
).read_text()
2731

2832

2933
def get_content(app):
@@ -109,8 +113,39 @@ def get_content(app):
109113
"padding."
110114
)
111115
),
112-
ExampleContainer(input_radio_check),
116+
ExampleContainer(
117+
load_source_with_environment(
118+
input_radio_check_source, "inputs", {"app": app}
119+
)
120+
),
113121
HighlightedSource(input_radio_check_source),
122+
html.P(
123+
dcc.Markdown(
124+
"Use the `inline` keyword to make the radioitems or "
125+
"checklists fit next to each other on a line."
126+
)
127+
),
128+
ExampleContainer(inline_inputs),
129+
HighlightedSource(input_radio_check_inline_source),
130+
html.P(
131+
dcc.Markdown(
132+
"If you need more granular control over checkboxes "
133+
"and radio buttons, you can also create standalone "
134+
"components. Bind callbacks to the `checked` keyword "
135+
"to react to changes in the input state. To attach "
136+
"a label, create a FormGroup with `check=True` and "
137+
"use the label's `html_for` keyword to bind it to "
138+
"the checkbox."
139+
)
140+
),
141+
ExampleContainer(
142+
load_source_with_environment(
143+
input_radio_check_standalone_source,
144+
"standalone_radio_check",
145+
{"app": app},
146+
)
147+
),
148+
HighlightedSource(input_radio_check_standalone_source),
114149
ApiDoc(
115150
get_component_metadata("src/components/input/Input.js"),
116151
component_name="Input",
@@ -127,4 +162,12 @@ def get_content(app):
127162
get_component_metadata("src/components/input/Checklist.js"),
128163
component_name="Checklist",
129164
),
165+
ApiDoc(
166+
get_component_metadata("src/components/input/Checkbox.js"),
167+
component_name="Checkbox",
168+
),
169+
ApiDoc(
170+
get_component_metadata("src/components/input/RadioButton.js"),
171+
component_name="RadioButton",
172+
),
130173
]
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import dash_bootstrap_components as dbc
2+
import dash_html_components as html
3+
from dash.dependencies import Input, Output
24

35
radioitems = dbc.FormGroup(
46
[
@@ -9,6 +11,7 @@
911
{"label": "Option 2", "value": 2},
1012
],
1113
value=1,
14+
id="radioitems-input",
1215
),
1316
]
1417
)
@@ -22,36 +25,24 @@
2225
{"label": "Option 2", "value": 2},
2326
],
2427
values=[],
28+
id="checklist-input",
2529
),
2630
]
2731
)
2832

29-
inline_radioitems = dbc.FormGroup(
33+
inputs = html.Div(
3034
[
31-
dbc.Label("Choose one"),
32-
dbc.RadioItems(
33-
options=[
34-
{"label": "Option 1", "value": 1},
35-
{"label": "Option 2", "value": 2},
36-
],
37-
value=1,
38-
inline=True,
39-
),
35+
dbc.Form([radioitems, checklist]),
36+
html.P(id="radioitems-checklist-output"),
4037
]
4138
)
4239

43-
inline_checklist = dbc.FormGroup(
44-
[
45-
dbc.Label("Choose a bunch"),
46-
dbc.Checklist(
47-
options=[
48-
{"label": "Option 1", "value": 1},
49-
{"label": "Option 2", "value": 2},
50-
],
51-
values=[],
52-
inline=True,
53-
),
54-
]
55-
)
5640

57-
inputs = dbc.Form([radioitems, checklist, inline_radioitems, inline_checklist])
41+
@app.callback(
42+
Output("radioitems-checklist-output", "children"),
43+
[Input("radioitems-input", "value"), Input("checklist-input", "values")],
44+
)
45+
def on_form_change(radio_items_value, checklist_values):
46+
template = "Radio button {} and {} checklist items are selected."
47+
output_string = template.format(radio_items_value, len(checklist_values))
48+
return output_string
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import dash_bootstrap_components as dbc
2+
3+
inline_radioitems = dbc.FormGroup(
4+
[
5+
dbc.Label("Choose one"),
6+
dbc.RadioItems(
7+
options=[
8+
{"label": "Option 1", "value": 1},
9+
{"label": "Option 2", "value": 2},
10+
],
11+
value=1,
12+
inline=True,
13+
),
14+
]
15+
)
16+
17+
inline_checklist = dbc.FormGroup(
18+
[
19+
dbc.Label("Choose a bunch"),
20+
dbc.Checklist(
21+
options=[
22+
{"label": "Option 1", "value": 1},
23+
{"label": "Option 2", "value": 2},
24+
],
25+
values=[],
26+
inline=True,
27+
),
28+
]
29+
)
30+
31+
inline_inputs = dbc.Form([inline_radioitems, inline_checklist])
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import dash_bootstrap_components as dbc
2+
import dash_html_components as html
3+
from dash.dependencies import Input, Output
4+
5+
standalone_radio_check = html.Div(
6+
[
7+
dbc.FormGroup(
8+
[
9+
dbc.Checkbox(
10+
id="standalone-checkbox", className="form-check-input"
11+
),
12+
dbc.Label(
13+
"This is a checkbox",
14+
html_for="standalone-checkbox",
15+
className="form-check-label",
16+
),
17+
],
18+
check=True,
19+
),
20+
dbc.FormGroup(
21+
[
22+
dbc.RadioButton(
23+
id="standalone-radio", className="form-check-input"
24+
),
25+
dbc.Label(
26+
"This is a radio button",
27+
html_for="standalone-radio",
28+
className="form-check-label",
29+
),
30+
],
31+
check=True,
32+
),
33+
html.Br(),
34+
html.P(id="standalone-radio-check-output"),
35+
]
36+
)
37+
38+
39+
@app.callback(
40+
Output("standalone-radio-check-output", "children"),
41+
[
42+
Input("standalone-checkbox", "checked"),
43+
Input("standalone-radio", "checked"),
44+
],
45+
)
46+
def on_form_change(checkbox_checked, radio_checked):
47+
if checkbox_checked and radio_checked:
48+
return "Both checked."
49+
elif checkbox_checked or radio_checked:
50+
return "One checked."
51+
else:
52+
return "None checked."

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ exclude =
77
docs/components_page/components/fade/fade.py,
88
docs/components_page/components/form/feedback.py,
99
docs/components_page/components/input/simple.py,
10+
docs/components_page/components/input/radio_check_standalone.py,
11+
docs/components_page/components/input/radio_check.py,
1012
docs/components_page/components/input_group/button.py,
1113
docs/components_page/components/input_group/dropdown.py,
1214
docs/components_page/components/list_group/links.py,

src/components/input/Checkbox.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import {omit} from 'ramda';
4+
5+
6+
class Checkbox extends React.Component {
7+
constructor(props) {
8+
super(props);
9+
this.state = {checked: props.checked};
10+
}
11+
12+
componentWillReceiveProps(newProps) {
13+
this.setState({checked: newProps.checked});
14+
}
15+
16+
render() {
17+
const {checked} = this.state;
18+
19+
return (
20+
<input
21+
type="checkbox"
22+
checked={checked}
23+
{...omit(['checked', 'setProps'], this.props)}
24+
onClick={() => {
25+
if (this.props.setProps) {
26+
this.props.setProps({
27+
checked: !checked
28+
});
29+
} else {
30+
this.setState({checked: !checked});
31+
}
32+
}}
33+
/>
34+
);
35+
}
36+
}
37+
38+
Checkbox.propTypes = {
39+
id: PropTypes.string,
40+
41+
/**
42+
* Whether RadioButton has been checked or not
43+
*/
44+
checked: PropTypes.bool,
45+
46+
/**
47+
* The class of the container (div)
48+
*/
49+
className: PropTypes.string,
50+
51+
/**
52+
* The style of the container (div)
53+
*/
54+
style: PropTypes.object,
55+
56+
/**
57+
* A unique identifier for the component, used to improve
58+
* performance by React.js while rendering components
59+
* See https://reactjs.org/docs/lists-and-keys.html for more info
60+
*/
61+
key: PropTypes.string,
62+
63+
/**
64+
* Dash-assigned callback that gets fired when the value changes.
65+
*/
66+
setProps: PropTypes.func
67+
};
68+
69+
export default Checkbox;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import {omit} from 'ramda';
4+
5+
6+
class RadioButton extends React.Component {
7+
constructor(props) {
8+
super(props);
9+
this.state = {checked: props.checked};
10+
}
11+
12+
componentWillReceiveProps(newProps) {
13+
this.setState({checked: newProps.checked});
14+
}
15+
16+
render() {
17+
const {checked} = this.state;
18+
19+
return (
20+
<input
21+
type="radio"
22+
checked={checked}
23+
{...omit(['checked', 'setProps'], this.props)}
24+
onClick={() => {
25+
if (this.props.setProps) {
26+
this.props.setProps({
27+
checked: !checked
28+
});
29+
} else {
30+
this.setState({checked: !checked});
31+
}
32+
}}
33+
/>
34+
);
35+
}
36+
}
37+
38+
RadioButton.propTypes = {
39+
id: PropTypes.string,
40+
41+
/**
42+
* Whether RadioButton has been checked or not
43+
*/
44+
checked: PropTypes.bool,
45+
46+
/**
47+
* The class of the container (div)
48+
*/
49+
className: PropTypes.string,
50+
51+
/**
52+
* The style of the container (div)
53+
*/
54+
style: PropTypes.object,
55+
56+
/**
57+
* A unique identifier for the component, used to improve
58+
* performance by React.js while rendering components
59+
* See https://reactjs.org/docs/lists-and-keys.html for more info
60+
*/
61+
key: PropTypes.string,
62+
63+
/**
64+
* Dash-assigned callback that gets fired when the value changes.
65+
*/
66+
setProps: PropTypes.func
67+
};
68+
69+
export default RadioButton;

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export {default as CardLink} from './components/card/CardLink';
1515
export {default as CardSubtitle} from './components/card/CardSubtitle';
1616
export {default as CardText} from './components/card/CardText';
1717
export {default as CardTitle} from './components/card/CardTitle';
18+
export {default as Checkbox} from './components/input/Checkbox';
1819
export {default as Checklist} from './components/input/Checklist';
1920
export {default as Col} from './components/layout/Col';
2021
export {default as Collapse} from './components/Collapse';
@@ -54,6 +55,7 @@ export {default as PopoverBody} from './components/PopoverBody';
5455
export {default as PopoverHeader} from './components/PopoverHeader';
5556
export {default as Progress} from './components/Progress';
5657
export {default as RadioItems} from './components/input/RadioItems';
58+
export {default as RadioButton} from './components/input/RadioButton';
5759
export {default as Row} from './components/layout/Row';
5860
export {default as Tab} from './components/tabs/Tab';
5961
export {default as Tabs} from './components/tabs/Tabs';

0 commit comments

Comments
 (0)