Skip to content

Commit 2a8721c

Browse files
glsdowntcbegley
andauthored
Add shorthand options support (#894)
* Move options and value to first two positional arguments * Add shorthand versions for the options parameter * Add documentation * Add js tests * Simplify example output for tests * Add shorthand example test * Add python test for positional args * Add docs to formatting exceptions * Update version * Add demo python app for testing * Remove julia and R examples * tidy Co-authored-by: tcbegley <[email protected]>
1 parent c38c943 commit 2a8721c

File tree

14 files changed

+753
-135
lines changed

14 files changed

+753
-135
lines changed

demo/app.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import dash_bootstrap_components as dbc
2+
from dash import Dash
3+
4+
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
5+
6+
test_content = dbc.Alert("Use this as a test playground", color="success")
7+
8+
app.layout = dbc.Container(test_content, className="p-5")
9+
10+
if __name__ == "__main__":
11+
app.run_server(debug=True)

docs/components_page/components/__tests__/test_input.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def check_input_simple_callbacks(runner):
3333
# --------------------------------
3434

3535

36-
def test_r_input_radio_ckeck(dashr):
36+
def test_r_input_radio_check(dashr):
3737
r_app = load_r_app((HERE.parent / "input" / "radio_check.R"), "inputs")
3838
dashr.start_server(r_app)
3939
check_input_radio_check_callbacks(dashr)

docs/components_page/components/input.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,29 @@ Set `inline=True` to make the radio items or checklists fit next to each other o
7373

7474
{{example:components/input/radio_check_inline.py:inline_inputs}}
7575

76+
## Shorthand options for Select, RadioItems and Checklist
77+
78+
In Python, the options in `Select`, `RadioItems` and `Checklist` components can be specified using more compact syntax. The standard structure is an array of dictionaries with defined keys:
79+
80+
```python
81+
options=[
82+
{
83+
"label": "Label 1",
84+
"value": "value_1",
85+
...
86+
},
87+
{
88+
"label": "Label 2",
89+
"value": "value_2",
90+
...
91+
},
92+
]
93+
```
94+
95+
This structure is required if you wish to use any of the additional `options` properties such as `disabled`. However, if you want to create a simple component, you can make use of the shorthand options below.
96+
97+
{{example:components/input/short_hand.py:short_hand}}
98+
7699
## Checked item styles
77100

78101
Use the `input_checked_style`, `inputCheckedClassName`, `label_checked_style` and `labelCheckedClassName` arguments to apply different styles to the labels of checked items.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import dash_bootstrap_components as dbc
2+
from dash import Input, Output, html
3+
4+
# `options` is provided as an array of dictionaries
5+
checklist = html.Div(
6+
[
7+
dbc.Checklist(
8+
[
9+
{"label": "Option 1", "value": 1},
10+
{"label": "Option B", "value": 2},
11+
{"label": "Option III", "value": 3},
12+
{"label": "4", "value": 4},
13+
],
14+
[3],
15+
id="shorthand-checklist",
16+
),
17+
],
18+
className="py-2",
19+
)
20+
21+
# All items in this list will have the value the same as the label
22+
select = html.Div(
23+
dbc.Select(
24+
["Option 1", "Option B", "Option III", 4],
25+
"Option III",
26+
id="shorthand-select",
27+
),
28+
className="py-2",
29+
)
30+
31+
# `options` is provided as value: label pairs - value must be a string
32+
radioitems = html.Div(
33+
[
34+
dbc.RadioItems(
35+
{
36+
"1": "Option 1",
37+
"2": "Option B",
38+
"3": "Option III",
39+
"4": 4,
40+
},
41+
"3",
42+
id="shorthand-radio",
43+
),
44+
],
45+
className="py-2",
46+
)
47+
48+
short_hand = html.Div(
49+
[
50+
dbc.Form([checklist, select, radioitems]),
51+
html.P(id="shorthand-output"),
52+
]
53+
)
54+
55+
56+
@app.callback(
57+
Output("shorthand-output", "children"),
58+
[
59+
Input("shorthand-checklist", "value"),
60+
Input("shorthand-select", "value"),
61+
Input("shorthand-radio", "value"),
62+
],
63+
)
64+
def on_form_change(checklist_value, select_value, radio_items_value):
65+
66+
checklist = ", ".join([str(c) for c in checklist_value])
67+
68+
output = (
69+
f"Checklist: [{checklist}], Selected: {select_value}, ",
70+
f"Radio: {radio_items_value}",
71+
)
72+
73+
return output

package-lock.json

Lines changed: 18 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ exclude =
1919
docs/components_page/components/input/simple.py,
2020
docs/components_page/components/input/radio_check_standalone.py,
2121
docs/components_page/components/input/radio_check.py,
22+
docs/components_page/components/input/short_hand.py,
2223
docs/components_page/components/input/colorpicker.py,
2324
docs/components_page/components/input_group/button.py,
2425
docs/components_page/components/input_group/dropdown.py,

src/components/input/Checklist.js

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
33
import {append, includes, without} from 'ramda';
44
import classNames from 'classnames';
55

6+
import {sanitizeOptions} from '../../private/util';
7+
68
/**
79
* Checklist is a component that encapsulates several checkboxes.
810
* The values and labels of the checklist is specified in the `options`
@@ -113,7 +115,8 @@ const Checklist = props => {
113115
</div>
114116
);
115117
};
116-
const items = options.map(option => listItem(option));
118+
119+
const items = sanitizeOptions(options).map(option => listItem(option));
117120

118121
return (
119122
<div
@@ -132,47 +135,90 @@ const Checklist = props => {
132135

133136
Checklist.propTypes = {
134137
/**
135-
* The ID of this component, used to identify dash components in callbacks.
136-
* The ID needs to be unique across all of the components in an app.
137-
*/
138-
id: PropTypes.string,
139-
140-
/**
141-
* An array of options
138+
* The options to display as items in the component. This can be an array
139+
* or a dictionary as follows:
140+
*
141+
* \n1. Array of options where the label and the value are the same thing -
142+
* [string|number]
143+
*
144+
* \n2. An array of options
145+
* ```
146+
* {
147+
* "label": [string|number],
148+
* "value": [string|number],
149+
* "disabled": [bool] (Optional),
150+
* "input_id": [string] (Optional),
151+
* "label_id": [string] (Optional)
152+
* }
153+
* ```
154+
*
155+
* \n3. Simpler `options` representation in dictionary format. The order is not
156+
* guaranteed. All values and labels will be treated as strings.
157+
* ```
158+
* {"value1": "label1", "value2": "label2", ... }
159+
* ```
160+
* which is equal to
161+
* ```
162+
* [
163+
* {"label": "label1", "value": "value1"},
164+
* {"label": "label2", "value": "value2"}, ...
165+
* ]
166+
* ```
142167
*/
143-
options: PropTypes.arrayOf(
144-
PropTypes.exact({
145-
/**
146-
* The checkbox's label
147-
*/
148-
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
149-
.isRequired,
150-
151-
/**
152-
* The value of the checkbox. This value corresponds to the items
153-
* specified in the `value` property.
154-
*/
155-
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
156-
.isRequired,
157-
158-
/**
159-
* If true, this checkbox is disabled and can't be clicked on.
160-
*/
161-
disabled: PropTypes.bool,
162-
163-
/**
164-
* id for this option's input, can be used to attach tooltips or apply
165-
* CSS styles
166-
*/
167-
input_id: PropTypes.string,
168-
169-
/**
170-
* id for this option's label, can be used to attach tooltips or apply
171-
* CSS styles
172-
*/
173-
label_id: PropTypes.string
174-
})
175-
),
168+
options: PropTypes.oneOfType([
169+
/**
170+
* Array of options where the label and the value are the same thing -
171+
* [string|number]
172+
*/
173+
PropTypes.arrayOf(
174+
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
175+
),
176+
/**
177+
* Simpler `options` representation in dictionary format. The order is not
178+
* guaranteed. All values and labels will be treated as strings.
179+
* {`value1`: `label1`, `value2`: `label2`, ... }
180+
* which is equal to
181+
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
182+
*/
183+
PropTypes.object,
184+
/**
185+
* An array of options {label: [string|number], value: [string|number]},
186+
* an optional disabled field can be used for each option
187+
*/
188+
PropTypes.arrayOf(
189+
PropTypes.exact({
190+
/**
191+
* The checkbox's label
192+
*/
193+
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
194+
.isRequired,
195+
196+
/**
197+
* The value of the checkbox. This value corresponds to the items
198+
* specified in the `value` property.
199+
*/
200+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
201+
.isRequired,
202+
203+
/**
204+
* If true, this checkbox is disabled and can't be clicked on.
205+
*/
206+
disabled: PropTypes.bool,
207+
208+
/**
209+
* id for this option's input, can be used to attach tooltips or apply
210+
* CSS styles
211+
*/
212+
input_id: PropTypes.string,
213+
214+
/**
215+
* id for this option's label, can be used to attach tooltips or apply
216+
* CSS styles
217+
*/
218+
label_id: PropTypes.string
219+
})
220+
)
221+
]),
176222

177223
/**
178224
* The currently selected value
@@ -181,6 +227,12 @@ Checklist.propTypes = {
181227
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
182228
),
183229

230+
/**
231+
* The ID of this component, used to identify dash components in callbacks.
232+
* The ID needs to be unique across all of the components in an app.
233+
*/
234+
id: PropTypes.string,
235+
184236
/**
185237
* The class of the container (div)
186238
*/

0 commit comments

Comments
 (0)