Skip to content

Shorthand supports for dash-core-components #1763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Sep 21, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,148 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- [#1763](https://github.com/plotly/dash/pull/1763):
## Dash and Dash Renderer

- `Input`, `State`, and `Output` now accept components instead of ID strings and Dash `callback` will auto-generate the component's ID under-the-hood if not supplied. This allows usage like:

```python
my_input = dcc.Input()
my_output = html.Div()
app.layout = html.Div([my_input, my_output])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'
```

Or, if using Python >=3.8 you can use the `:=` walrus operator:
```python
app.layout = html.Div([
my_input := dcc.Input(),
my_output := html.Div()
])

@dash.callback(Output(my_output, 'children'), Input(my_input, 'value'))
def update(value):
return f'You have entered {value}'

```
## Dash Core Components

### Rearranged Keyword Arguments & Flexible Types
**`Dropdown`, `RadioItem`, and `Checklist`**
- Rearranged Keyword Arguments - `options` & `value` are now the first two keywords which means they can be supplied as positional arguments without the keyword. Supplying the keywords (`options=` and `value=`) is still supported.
- Flexible Types - `options` can be supplied in two new forms:
1. An array of `string|number|bool` where `label` and `value` are equal to the items in the list.
2. A dictionary where the keys and values set as `value` and `label` respectively.

Before:

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'New York'},
{'label': 'Montreal', 'value': 'Montreal'},
],
value='New York'
)
```
or

```python
dcc.Dropdown(
options=[
{'label': 'New York', 'value': 'NYC'},
{'label': 'Montreal', 'value': 'MTL'},
],
value='New York'
)
```

After:

```python
dcc.Dropdown(['New York', 'Montreal'], 'New York')
```
Or

```python
dcc.Dropdown({'NYC': 'New York', 'MTL': 'Montreal'}, 'New York')
```

**`RangeSlider` & `Slider`**
- Rearranged Keyword Arugments - `min`, `max`, and `step` are now the first three keyword arguments which means they can be supplied as positional arguments without the keyword.
- Flexible Types
- `step` will be calculated implicitly if not given.
- `marks` will be auto generated if not given. It will use `min` and `max` and will respect `step` if supplied. Auto generated marks labels are SI unit formatted. Around 5 human-readable marks will be created.
- To remove the Slider's marks, set `marks=None`.

Before:

```python
dcc.Slider(marks={1: 2, 2: 2, 3: 3})
```

After:
```python
dcc.Slider(min=1, max=3, step=1)
```
Or equivalently:
```python
dcc.Slider(1, 3, 1)
```
Step can also be omitted and the `Slider` will attempt to create a nice, human readable step with SI units and around 5 marks:
```python
dcc.Slider(0, 100)
```

The SI units used in `marks` are:

* `z` - zepto, 10⁻²¹
* `a` - atto, 10⁻¹⁸
* `f` - femto, 10⁻¹⁵
* `p` - pico, 10⁻¹²
* `n` - nano, 10⁻⁹
* `µ` - micro, 10⁻⁶
* `m` - milli, 10⁻³
* `​` (none) - 10⁰
* `k` - kilo, 10³
* `M` - mega, 10⁶
* `G` - giga, 10⁹
* `T` - tera, 10¹²
* `P` - peta, 10¹⁵
* `E` - exa, 10¹⁸
* `Z` - zetta, 10²¹

**`DataTable`**

- Rearranged Keyword Arguments - `data` and `columns` the first twokeyword arguments which means they can be supplied as positional arguments without the keyword.
- Inferred Properties - If `columns` isn't supplied then it is extracted from the the first row in `data`

Before:

```python
dash_table.DataTable(data=df.to_dict('records'), columns=[{'name': i, 'id': i} for i in df.columns])
```
After:

```python
dash_table.DataTable(data=df.to_dict('records'))
```

### New Component Properties

**`Checklist` & `RadioItems`**

- A new property `inline` appends `display: inline-block` to `labelStyle`.

```python
dcc.Checklist(inline=True)
```

## [2.0.0] - 2021-08-03

## Dash and Dash Renderer
Expand Down
170 changes: 114 additions & 56 deletions components/dash-core-components/src/components/Checklist.react.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import {append, includes, without} from 'ramda';
import React, {Component} from 'react';
import {sanitizeOptions} from '../utils/optionTypes';

/**
* Checklist is a component that encapsulates several checkboxes.
Expand All @@ -22,8 +23,8 @@ export default class Checklist extends Component {
style,
loading_state,
value,
inline,
} = this.props;

return (
<div
data-dash-is-loading={
Expand All @@ -33,77 +34,126 @@ export default class Checklist extends Component {
style={style}
className={className}
>
{options.map(option => (
<label
key={option.value}
style={labelStyle}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without([option.value], value);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
))}
{sanitizeOptions(options).map(option => {
return (
<label
key={option.value}
style={Object.assign(
{},
labelStyle,
inline ? {display: 'inline-block'} : {}
)}
className={labelClassName}
>
<input
checked={includes(option.value, value)}
className={inputClassName}
disabled={Boolean(option.disabled)}
style={inputStyle}
type="checkbox"
onChange={() => {
let newValue;
if (includes(option.value, value)) {
newValue = without(
[option.value],
value
);
} else {
newValue = append(option.value, value);
}
setProps({value: newValue});
}}
/>
{option.label}
</label>
);
})}
</div>
);
}
}

Checklist.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* An array of options
*/
options: PropTypes.arrayOf(
PropTypes.exact({
/**
* The checkbox's label
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* The value of the checkbox. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,

/**
* If true, this checkbox is disabled and can't be clicked on.
*/
disabled: PropTypes.bool,
})
),
options: PropTypes.oneOfType([
/**
* Array of options where the label and the value are the same thing - [string|number|bool]
*/
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),
/**
* Simpler `options` representation in dictionary format. The order is not guaranteed.
* {`value1`: `label1`, `value2`: `label2`, ... }
* which is equal to
* [{label: `label1`, value: `value1`}, {label: `label2`, value: `value2`}, ...]
*/
PropTypes.object,
/**
* An array of options {label: [string|number], value: [string|number]},
* an optional disabled field can be used for each option
*/
PropTypes.arrayOf(
PropTypes.exact({
/**
* The option's label
*/
label: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* The value of the option. This value
* corresponds to the items specified in the
* `value` property.
*/
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
]).isRequired,

/**
* 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,
})
),
]),

/**
* The currently selected value
*/
value: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number])
PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.bool,
])
),

/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: PropTypes.string,

/**
* The class of the container (div)
*/
Expand Down Expand Up @@ -187,6 +237,13 @@ Checklist.propTypes = {
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

/**
* Indicates whether labelStyle should be inline or not
* True: Automatically set { 'display': 'inline-block' } to labelStyle
* False: No additional styles are passed into labelStyle.
*/
inline: PropTypes.bool,
};

Checklist.defaultProps = {
Expand All @@ -198,4 +255,5 @@ Checklist.defaultProps = {
value: [],
persisted_props: ['value'],
persistence_type: 'local',
inline: false,
};
Loading