-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Dash UI customization example (#273)
* Add example dash apps (1-4) * Add example dash app (5) * Add README.md * Address the feedback * Revert the first app to its previous version
- Loading branch information
Showing
16 changed files
with
458 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# Comprehensive Guide to Dash UI Customization | ||
|
||
Explore various methods to customize your Dash app UI, from using styling with CSS and Dash Bootstrap Components, to building custom Dash components to achieve advanced customization using React. | ||
|
||
1. Inline CSS Styling | ||
2. Using Your Own CSS Files | ||
3. Dash Bootstrap Components | ||
4. Custom Callbacks for Dynamic Styling | ||
5. Building Custom Dash Components | ||
|
||
Each section includes simple scripts that feature a title and a button, demonstrating how these methods can be practically applied to enhance your application's aesthetics and functionality. | ||
|
||
## Methods for UI Customization in Dash | ||
### 1. Inline CSS Styling | ||
#### Example Code | ||
- `app_inline.py` | ||
|
||
### 2. Using Your Own CSS Files | ||
#### Example Code | ||
- `app_css.py` | ||
- `assets/style.css` | ||
|
||
### 3. Dash Bootstrap Components | ||
#### Prerequisites | ||
- Run `pip install dash-bootstrap-components` | ||
#### Example Code | ||
- `app_bootstrap.py` | ||
|
||
### 4. Custom Callbacks for Dynamic Styling | ||
#### Prerequisites | ||
- Run `pip install dash-bootstrap-components` (note: `dash-bootstrap-components` is not required to implement custom callback functions, but it is used in `app_callback.py` as it is implemented on top of `app_bootstrap.py`.) | ||
#### Example Code | ||
- `app_callback.py` | ||
|
||
|
||
### 5. Building Custom Dash Components | ||
#### Step-by-Step | ||
- Download and install Node.js and npm from the [Node.js official website](https://nodejs.org/en) | ||
- Run `pip install cookiecutter` | ||
- Run `pip install virtualenv` | ||
- Run `cookiecutter gh:plotly/dash-component-boilerplate` | ||
- Navigate into the newly created `<project_shortname>` directory and update `usage.py` for your Dash app and `src/lib/components/<component_name>.react.js` for your custom component | ||
- `npm run build` to compile | ||
- Run `python usage.py` and check out your Dash app | ||
|
||
Note: To learn more about the `dash-component-boilerplate`, refer to [here](https://github.com/plotly/dash-component-boilerplate). | ||
|
||
#### Example Code for Custom Button Component and Deployment on Ploomber Cloud | ||
- `custom_component/app_custom.py`: Main script for the Dash app (can replace the `usage.py` file) | ||
- `custom_component/CustomButton.react.js`: Main source code for the custom component (can replace the `src/lib/components/<component_name>.react.js` file) | ||
- `custom_component/requirements.txt`: For deployment on Ploomber Cloud | ||
- `custom_component/demo/`: The generated Python scripts required for deployment on Ploomber Cloud | ||
|
||
|
||
## Deployment on Ploomber Cloud | ||
|
||
To deploy your Dash app on Ploomber Cloud, you need: | ||
|
||
- `app.py` | ||
- `requirements.txt` | ||
|
||
You should ensure your Dash app script is called `app.py`. Rename the one you want to deploy to `app.py`. Also, your `requirements.txt` should be: | ||
|
||
```sh | ||
dash | ||
dash-bootstrap-components | ||
``` | ||
|
||
Note: If you're deploying the last example that includes a custom component, ensure you also include the auto-generated scripts required for proper functionality: | ||
- `<project_shortname>/__init__.py` | ||
- `<project_shortname>/_imports_.py` | ||
- `<project_shortname>/<component_name>.py` | ||
- `<project_shortname>/<project_shortname>.min.js` | ||
- `<project_shortname>/<project_shortname>.min.js.map` | ||
- `<project_shortname>/package-info.json` | ||
|
||
### Graphical User Interface (GUI) | ||
|
||
Log into your [Ploomber Cloud account](https://www.platform.ploomber.io/applications). | ||
|
||
Click the NEW button to start the deployment process: | ||
|
||
![GUI Deployment](assets/gui_deploy1.png) | ||
|
||
Select the Dash option, and upload your code as a zip file in the source code section: | ||
|
||
![GUI Deployment](assets/gui_deploy2.png) | ||
|
||
After optionally customizing some settings, click `CREATE`. | ||
|
||
### Command Line Interface (CLI) | ||
|
||
If you haven't installed `ploomber-cloud`, run: | ||
```sh | ||
pip install ploomber-cloud | ||
``` | ||
|
||
Set your API key following [this documentation](https://docs.cloud.ploomber.io/en/latest/quickstart/apikey.html). | ||
```sh | ||
ploomber-cloud key YOURKEY | ||
``` | ||
|
||
Navigate to your project directory where your files are located: | ||
```sh | ||
cd <project-name> | ||
``` | ||
|
||
Then, initialize the project and confirm the inferred project type (Dash) when prompted: | ||
```sh | ||
(testing_dash_app) ➜ ploomber-cloud init ✭ ✱ | ||
Initializing new project... | ||
Inferred project type: 'dash' | ||
Is this correct? [y/N]: y | ||
Your app '<id>' has been configured successfully! | ||
To configure resources for this project, run 'ploomber-cloud resources' or to deploy with default configurations, run 'ploomber-cloud deploy' | ||
``` | ||
|
||
Deploy your application and monitor the deployment at the provided URL: | ||
|
||
```sh | ||
(testing_dash_app) ➜ cloud ploomber-cloud deploy ✭ ✱ | ||
Compressing app... | ||
Adding app.py... | ||
Ignoring file: ploomber-cloud.json | ||
Adding requirements.txt... | ||
App compressed successfully! | ||
Deploying project with id: <id>... | ||
The deployment process started! Track its status at: https://www.platform.ploomber.io/applications/<id>/<job_id> | ||
``` | ||
|
||
For more details, refer to this [documentation](https://docs.cloud.ploomber.io/en/latest/user-guide/cli.html). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from dash import Dash, html | ||
import dash_bootstrap_components as dbc | ||
|
||
app = Dash(__name__, external_stylesheets=[dbc.themes.MINTY]) | ||
server = app.server | ||
|
||
app.layout = dbc.Container([ | ||
dbc.Row(dbc.Col(html.H1('Welcome to Dash Customization Demo!', className='text-center my-4'), width=12)), | ||
dbc.Row(dbc.Col(dbc.Button('Click me!', color='primary', className='w-100 mt-4 mb-4'), width=12)) | ||
], fluid=True) | ||
|
||
if __name__ == '__main__': | ||
app.run_server(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from dash import Dash, html, Input, Output | ||
import dash_bootstrap_components as dbc | ||
|
||
app = Dash(__name__, external_stylesheets=[dbc.themes.MINTY]) | ||
server = app.server | ||
|
||
app.layout = dbc.Container([ | ||
dbc.Row(dbc.Col(html.H1('Welcome to Dash Customization Demo!', className='text-center my-4'), width=12)), | ||
dbc.Row(dbc.Col(dbc.Button('Click me!', id='btn', n_clicks=0, color='primary', className='w-100 mt-4 mb-4'), width=12)) | ||
], fluid=True) | ||
|
||
@app.callback(Output('btn', 'color'), Input('btn', 'n_clicks')) | ||
def update_style(n_clicks): | ||
colors = ['primary', 'secondary', 'warning'] | ||
return colors[n_clicks % 3] | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run_server(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from dash import html, Dash | ||
|
||
app = Dash(__name__) | ||
server = app.server | ||
|
||
app.layout = html.Div([ | ||
html.H1( | ||
'Welcome to Dash Customization Demo!', | ||
className='center-text' | ||
), | ||
html.Button('Click me 1!', className='button green'), | ||
html.Button('Click me 2!', className='button blue'), | ||
html.Button('Click me 3!', className='button red') | ||
], className='padding-20') | ||
|
||
if __name__ == '__main__': | ||
app.run_server(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from dash import html, Dash | ||
|
||
app = Dash(__name__) | ||
server = app.server | ||
|
||
app.layout = html.Div([ | ||
html.H1( | ||
'Welcome to Dash Customization Demo!', | ||
style={'textAlign': 'center'} | ||
), | ||
html.Button( | ||
'Click me!', | ||
style={ | ||
'width': '100%', | ||
'padding': '10px', | ||
'background': 'darkgreen', | ||
'color': 'white', | ||
'fontSize': '16px' | ||
} | ||
) | ||
], style={'padding': '20px'}) | ||
|
||
if __name__ == '__main__': | ||
app.run_server(debug=True) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
.button { | ||
width: 100%; | ||
padding: 10px; | ||
color: white; | ||
font-size: 16px; | ||
} | ||
|
||
.green { | ||
background: darkgreen; | ||
} | ||
|
||
.blue { | ||
background: blue; | ||
} | ||
|
||
.red { | ||
background: red; | ||
} | ||
|
||
.center-text { | ||
text-align: center; | ||
} | ||
|
||
.padding-20 { | ||
padding: 20px; | ||
} |
70 changes: 70 additions & 0 deletions
70
examples/dash/dash-customization/custom_component/CustomButton.react.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React, { useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
/** | ||
* CustomButton is an example component that shows a bouncing button. | ||
*/ | ||
const CustomButton = (props) => { | ||
// Extract the props | ||
const { id, label, color, className } = props; | ||
const [isBouncing, setIsBouncing] = useState(false); | ||
|
||
// Handle the button click event | ||
const handleClick = () => { | ||
setIsBouncing(true); | ||
setTimeout(() => setIsBouncing(false), 500); // Reset after animation | ||
}; | ||
|
||
// Determine Bootstrap color class based on the color prop | ||
const colorClass = color ? `btn-${color}` : ''; | ||
|
||
// Render the button | ||
return ( | ||
<button | ||
id={id} | ||
onClick={handleClick} | ||
className={`btn ${colorClass} ${className} ${isBouncing ? 'bouncing' : ''}`} | ||
style={{ | ||
padding: '10px 20px', | ||
border: 'none', | ||
borderRadius: '5px', | ||
backgroundColor: color ? '' : '#ff8c00', // Custom fallback color if no Bootstrap color is provided | ||
color: 'white', | ||
fontSize: '16px', | ||
cursor: 'pointer', | ||
outline: 'none', | ||
position: 'relative', | ||
transition: 'transform 0.3s ease', | ||
transform: isBouncing ? 'translateY(-10px)' : 'translateY(0)', | ||
}} | ||
> | ||
{label} | ||
</button> | ||
); | ||
} | ||
|
||
CustomButton.propTypes = { | ||
/** | ||
* The ID used to identify this component in Dash callbacks. | ||
*/ | ||
id: PropTypes.string, | ||
|
||
/** | ||
* A label that will be printed when this component is rendered. | ||
*/ | ||
label: PropTypes.string.isRequired, | ||
|
||
/** | ||
* The color of the button. | ||
* This should be a Bootstrap color name. | ||
*/ | ||
color: PropTypes.string, | ||
|
||
/** | ||
* The className of the button. | ||
* This is used to apply custom styles to the button. | ||
*/ | ||
className: PropTypes.string, | ||
}; | ||
|
||
export default CustomButton; |
17 changes: 17 additions & 0 deletions
17
examples/dash/dash-customization/custom_component/app_custom.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from demo import CustomButton | ||
from dash import Dash, html | ||
import dash_bootstrap_components as dbc | ||
|
||
app = Dash(__name__, external_stylesheets=[dbc.themes.MINTY]) | ||
server = app.server | ||
|
||
# Define the layout of the app using Bootstrap components | ||
app.layout = dbc.Container([ | ||
dbc.Row(dbc.Col(html.H1('Welcome to Dash Customization Demo!', className='text-center my-4'), width=12)), | ||
dbc.Row(dbc.Col( | ||
CustomButton(id='custom-button', label='Click Me!', color='primary', className='btn btn-primary w-100 mt-4 mb-4'), width=12)) | ||
], fluid=True) | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run_server(debug=True) |
44 changes: 44 additions & 0 deletions
44
examples/dash/dash-customization/custom_component/demo/CustomButton.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# AUTO GENERATED FILE - DO NOT EDIT | ||
|
||
from dash.development.base_component import Component, _explicitize_args | ||
|
||
|
||
class CustomButton(Component): | ||
"""A CustomButton component. | ||
CustomButton is an example component that shows a bouncing button. | ||
Keyword arguments: | ||
- id (string; optional): | ||
The ID used to identify this component in Dash callbacks. | ||
- className (string; optional): | ||
The className of the button. This is used to apply custom styles | ||
to the button. | ||
- color (string; optional): | ||
The color of the button. This should be a Bootstrap color name. | ||
- label (string; required): | ||
A label that will be printed when this component is rendered.""" | ||
_children_props = [] | ||
_base_nodes = ['children'] | ||
_namespace = 'demo' | ||
_type = 'CustomButton' | ||
@_explicitize_args | ||
def __init__(self, id=Component.UNDEFINED, label=Component.REQUIRED, color=Component.UNDEFINED, className=Component.UNDEFINED, **kwargs): | ||
self._prop_names = ['id', 'className', 'color', 'label'] | ||
self._valid_wildcard_attributes = [] | ||
self.available_properties = ['id', 'className', 'color', 'label'] | ||
self.available_wildcard_properties = [] | ||
_explicit_args = kwargs.pop('_explicit_args') | ||
_locals = locals() | ||
_locals.update(kwargs) # For wildcard attrs and excess named props | ||
args = {k: _locals[k] for k in _explicit_args} | ||
|
||
for k in ['label']: | ||
if k not in args: | ||
raise TypeError( | ||
'Required argument `' + k + '` was not specified.') | ||
|
||
super(CustomButton, self).__init__(**args) |
Oops, something went wrong.