Skip to content
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

Feature/plotly theming #6

Merged
merged 22 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
36bec62
Feat(plotly_theme): added theme and configuration options for plotly …
GedionT Oct 14, 2024
53b5f1e
Feat(plotly_components): added plotly chart components
GedionT Oct 14, 2024
a57d6d1
Feat(st_undp): apply plotly theme and export components
GedionT Oct 14, 2024
3ced4a9
Feat(integration): added an integration page for charts
GedionT Oct 14, 2024
5e8ddb6
Fix(app): added integration page to navigation
GedionT Oct 14, 2024
2c784cb
Feat(lib): added plotly dependency in poetry
GedionT Oct 14, 2024
ecb6e52
Fix(plotly-menu): underlined animation effect fixed for plotly chart …
GedionT Oct 14, 2024
13752cc
Fix(link-animation): link underline animation on hover fixed
GedionT Oct 14, 2024
6342f71
Fix(checkbox): removed custom icon for checkbox
GedionT Oct 14, 2024
ee93055
Chore(poetry): update lock file
GedionT Oct 14, 2024
0afad5d
Refactor(standard): removed unused module
GedionT Oct 14, 2024
7932e71
Fix(style): code padding and visited link fix
GedionT Oct 15, 2024
96561db
Feat(standard): added expander to text tab
GedionT Oct 15, 2024
c0ab272
Docs(readme): added plotly feature to readme
GedionT Oct 15, 2024
81ac71f
Refactor(app): renamed integration -> extras
GedionT Nov 25, 2024
157b77e
Chore(plotly_components): removed plotly components in place of solid…
GedionT Nov 25, 2024
28e9813
Refactor(extras): rename integrations to extras
GedionT Nov 25, 2024
e3a5f93
Refactor(plotly_theme): cleaned up and modularized plotly theme
GedionT Nov 25, 2024
5fa3012
Refactor(__init__): conditionally apply plotly theme
GedionT Nov 25, 2024
c6091ef
Docs(main.scss): added brief comments
GedionT Nov 25, 2024
b07e815
Feat(plotly-theme): added a json configuration file for Plotly
GedionT Nov 25, 2024
de45948
Feat(undp-colors): added a color config file for reference
GedionT Nov 25, 2024
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
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ python -m st_undp configure
```

This will edit the theme section in the file if it exists or create it if it doesn't.
Then, call `apply_style` function inside your application entry point, typically `app.py`:
Then, call `apply_style` function inside your application entry point, typically `app.py`:

```python
import st_undp
Expand All @@ -63,6 +63,7 @@ This package is currently in the early stages of development. Main features incl
- `ProximaNova` fonts
- CSS styles for most Streamlit input components
- Several custom components from the UNDP Design System, such as Author, Footer, Header, Stats Card
- Custom UNDP themed for Plotly and ready-made Plotly charts.
- No additional dependencies on top of what is required by `streamlit`

## Contributing
Expand All @@ -81,16 +82,16 @@ routine operations.

## License

This project is licensed under the BSD 3-Clause License. However, entities or individuals not affiliated with UNDP
are strictly prohibited from using this package or any of its components to create, share, publish, or distribute works
This project is licensed under the BSD 3-Clause License. However, entities or individuals not affiliated with UNDP
are strictly prohibited from using this package or any of its components to create, share, publish, or distribute works
that resemble, claim affiliation with, or imply endorsement by UNDP.

UNDP’s name, emblem and its abbreviation are the exclusive property of UNDP and are protected under international law.
Their unauthorized use is prohibited, and they may not be reproduced or used in any manner without UNDP’s prior written permission.
UNDP’s name, emblem and its abbreviation are the exclusive property of UNDP and are protected under international law.
Their unauthorized use is prohibited, and they may not be reproduced or used in any manner without UNDP’s prior written permission.

## Contact

This project is part of [Data Futures Exchange (DFx)](https://data.undp.org) at UNDP.
If you are facing any issues or would like to make some suggestions, feel free to
[open an issue](https://github.com/undp-data/st-undp/issues/new/choose).
If you are facing any issues or would like to make some suggestions, feel free to
[open an issue](https://github.com/undp-data/st-undp/issues/new/choose).
For enquiries about DFx, visit [Contact Us](https://data.undp.org/contact-us).
1 change: 1 addition & 0 deletions app.py
GedionT marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
title="custom components",
url_path="/custom",
),
st.Page(page="pages/extras.py", title="extras", url_path="/extras"),
st.Page(
page="pages/about.py",
title="about",
Expand Down
211 changes: 211 additions & 0 deletions pages/extras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
"""
extras component
eg. Plotly charts
"""
import streamlit as st

def insert_controls(prefix: str) -> dict:
"""
Takes in a string that serves as a prefix for the keys of the input controls, and
creates a set of input controls for Plotly chart customization.

Returns:
A dictionary containing the following keys and their corresponding user input values:
"""
controls_dict = {
"title": st.text_input(
"Chart Title", f"{prefix.capitalize()} chart", key=f"{prefix}_title"
),
"x_title": st.text_input("X Axis Title", "X Axis", key=f"{prefix}_x_title"),
"y_title": st.text_input("Y Axis Title", "Y Axis", key=f"{prefix}_y_title"),
"annotation": st.text_input(
"Annotation (e.g., Source)",
"Source: Example Data",
key=f"{prefix}_annotation",
),
"annotation_x": st.number_input(
"Annotation X Position (0 to 1)",
min_value=0.0,
max_value=1.0,
value=0.0,
key=f"{prefix}_annotation_x",
),
"annotation_y": st.number_input(
"Annotation Y Position (0 to 1)",
min_value=-1.0,
max_value=1.0,
value=-0.3,
key=f"{prefix}_annotation_y",
),
}
return controls_dict

tabs = st.tabs(
[
"Area Chart",
"Butterfly Chart",
"Donut Chart",
"Grouped Bar Chart",
"Heatmap",
"Simple Bar Chart",
"Stacked Bar Chart",
]
)

with tabs[0]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("area")
with col2:
area_data = {"x": [1, 2, 3, 4, 5], "y": [5, 10, 15, 20, 25]}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure(data=go.Scatter(x={area_data['x']}, y={area_data['y']}, fill='tozeroy'))
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[1]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("butterfly")
with col2:
butterfly_data = {
"categories": ["A", "B", "C"],
"values1": [10, 20, 30],
"values2": [15, 25, 35],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Bar(x={butterfly_data['values1']}, y={butterfly_data['categories']}, name='Group 1', orientation='h'))
fig.add_trace(go.Bar(x=[-v for v in {butterfly_data['values2']}], y={butterfly_data['categories']}, name='Group 2', orientation='h'))
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}', barmode='overlay')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[2]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("donut")
with col2:
donut_data = {
"labels": ["Category A", "Category B", "Category C", "Category D"],
"values": [4500, 2500, 1050, 2002],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure(data=[go.Pie(labels={donut_data['labels']}, values={donut_data['values']}, hole=0.4)])
fig.update_layout(title='{controls['title']}')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[3]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("grouped_bar")
with col2:
grouped_bar_data = {
"categories": ["Category 1", "Category 2", "Category 3"],
"groups": [
{"name": "Group 1", "values": [10, 20, 30]},
{"name": "Group 2", "values": [15, 25, 35]},
],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure()
for group in {grouped_bar_data['groups']}:
fig.add_trace(go.Bar(x={grouped_bar_data['categories']}, y=group['values'], name=group['name']))
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}', barmode='group')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[4]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("heatmap")
with col2:
heatmap_data = {
"z": [[1, 20, 30], [20, 1, 60], [30, 60, 1]],
"x": ["X1", "X2", "X3"],
"y": ["Y1", "Y2", "Y3"],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure(data=go.Heatmap(z={heatmap_data['z']}, x={heatmap_data['x']}, y={heatmap_data['y']}, colorscale=['#FFF4AC', '#C8E7A8', '#8CD8A4', '#47C79F', '#00B29C', '#0099A5', '#007FAF', '#0067AD', '#005396', '#003F80'], colorbar=dict(
orientation='h',
x=0.5,
y=1.1,
xanchor='center',
yanchor='bottom',
title='scale'
)))
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[5]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("simple bar")
with col2:
simple_bar_data = {
"categories": ["Category A", "Category B", "Category C"],
"values": [10, 20, 30],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure(data=[go.Bar(y={simple_bar_data['categories']}, x={simple_bar_data['values']})])
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)

with tabs[6]:
col1, col2 = st.columns(2)
with col1:
controls = insert_controls("stacked bar")
with col2:
stacked_bar_data = {
"categories": ["Category 1", "Category 2", "Category 3"],
"groups": [
{"name": "Group 1", "values": [10, 20, 30]},
{"name": "Group 2", "values": [15, 25, 35]},
],
}
code_body = f"""
import plotly.graph_objects as go
fig = go.Figure()
for group in {stacked_bar_data['groups']}:
fig.add_trace(go.Bar(x={stacked_bar_data['categories']}, y=group['values'], name=group['name']))
fig.update_layout(title='{controls['title']}', xaxis_title='{controls['x_title']}', yaxis_title='{controls['y_title']}', barmode='stack')
if '{controls['annotation']}' != '':
fig.add_annotation(text='{controls['annotation']}', xref='paper', yref='paper', x={controls['annotation_x']}, y={controls['annotation_y']}, showarrow=False)
st.plotly_chart(fig)"""
exec(code_body.strip())
with st.expander("Show Code"):
st.code(code_body)
8 changes: 6 additions & 2 deletions pages/standard.py
GedionT marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import streamlit as st

import st_undp

OPTIONS = list("ABCDE")

tab1, tab2, tab3, tab4, tab5 = st.tabs(
Expand Down Expand Up @@ -74,3 +72,9 @@
st.subheader("Subheader")
st.markdown("Regular text with an [example link](https://data.undp.org).")
st.text("Do not use `st.text`. Its content will not be properly styled.")
st.markdown("Code sections looks as follows `print(f'hello streamlit app')` ")
for item in [
{'header': 'An expander', 'body': 'This is an expander with an [example link](https://data.undp.org)'}
]:
with st.expander(item['header']):
st.write(item['body'])
Loading