Skip to content

Commit

Permalink
Merge branch 'main' into test/update_vizro_ai_integration_test
Browse files Browse the repository at this point in the history
  • Loading branch information
Anna-Xiong authored Jun 17, 2024
2 parents a6062cc + 4507eb8 commit 44b7810
Show file tree
Hide file tree
Showing 15 changed files with 197 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Highlights ✨
- A bullet item for the Highlights ✨ category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Removed
- A bullet item for the Removed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Added
- A bullet item for the Added category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->

### Changed

- Move changing theme callback to the client-side. ([#523](https://github.com/mckinsey/vizro/pull/523))

<!--
### Deprecated
- A bullet item for the Deprecated category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Fixed
- A bullet item for the Fixed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Security
- A bullet item for the Security category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Highlights ✨
- A bullet item for the Highlights ✨ category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Removed
- A bullet item for the Removed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Added
- A bullet item for the Added category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Changed
- A bullet item for the Changed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Deprecated
- A bullet item for the Deprecated category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Fixed
- A bullet item for the Fixed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
<!--
### Security
- A bullet item for the Security category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))
-->
10 changes: 6 additions & 4 deletions vizro-core/docs/pages/user-guides/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ The below sections are guides on how to use pre-defined action functions.
To enable downloading data, you can add the [`export_data`][vizro.actions.export_data] action function to the [`Button`][vizro.models.Button] component.
Hence, as a result, when a dashboard user now clicks the button, all data on the page will be downloaded.

When data from a [custom chart](custom-charts.md) is exported it is the contents of the `data_frame` input argument that is exported.
Therefore, the exported data will reflect any native filters and parameters, but no transformations to the `data_frame` done inside the chart function.


!!! example "`export_data`"

=== "app.py"
Expand Down Expand Up @@ -104,6 +100,12 @@ Therefore, the exported data will reflect any native filters and parameters, but

[Graph]: ../../assets/user_guides/actions/actions_export.png


!!! note

Note that exported data only reflects the original dataset and any native data modifications defined with [`vm.Filter`](filters.md), [`vm.Parameter`](data.md/#parametrize-data-loading) or [`filter_interaction`](actions.md/#filter-data-by-clicking-on-chart) action.
Filters from the chart itself, such as ag-grid filters, are not included, and neither are other chart modifications, nor any data transformations in custom charts.

### Filter data by clicking on chart

To enable filtering when clicking on data in a source chart, you can add the
Expand Down
54 changes: 44 additions & 10 deletions vizro-core/examples/_dev/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,63 @@
import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro.tables import dash_ag_grid, dash_data_table
from vizro.actions import export_data
from vizro.models.types import capture
from vizro.tables import dash_ag_grid

df = px.data.gapminder()

# Vizro filter exporting Page --------------------------------------------
# Solution based on https://vizro.readthedocs.io/en/stable/pages/user-guides/actions/#export-data

page_one = vm.Page(
title="Dash AG Grid",
layout=vm.Layout(grid=[[0, 1]], col_gap="0px"),
title="Vizro filters exporting",
layout=vm.Layout(grid=[[0]] * 5 + [[1]]),
components=[
vm.AgGrid(title="Equal Title One", figure=dash_ag_grid(data_frame=df)),
vm.Graph(figure=px.box(df, x="continent", y="lifeExp", title="Equal Title One")),
vm.AgGrid(id="ag_grid_1", title="Equal Title One", figure=dash_ag_grid(data_frame=df)),
vm.Button(text="Export data", actions=[vm.Action(function=export_data())]),
],
controls=[vm.Filter(column="continent"), vm.Filter(column="year")],
)

# AgGrid filter exporting Page -------------------------------------------
# Solution based on https://dash.plotly.com/dash-ag-grid/export-data-csv


# More about Vizro Custom Actions -> https://vizro.readthedocs.io/en/stable/pages/user-guides/custom-actions/
@capture("action")
def ag_grid_data_exporting():
"""Custom Action."""
return True


page_two = vm.Page(
title="Dash Data Table",
layout=vm.Layout(grid=[[0, 1]]),
title="AgGrid filters exporting",
layout=vm.Layout(grid=[[0]] * 5 + [[1]]), # grid = [[0], [0], [0], [0], [0], [1]]
components=[
vm.Table(title="Equal Title One", figure=dash_data_table(data_frame=df)),
vm.Graph(figure=px.box(df, x="continent", y="lifeExp", title="Equal Title One")),
vm.AgGrid(
id="ag_grid_2",
title="Equal Title One",
figure=dash_ag_grid(
# underlying_ag_grid_2 is the id of the AgGrid component on the client-side. It is used to reference
# it's `exportDataAsCsv` property with the custom action below
id="underlying_ag_grid_2",
data_frame=df,
csvExportParams={
"fileName": "ag_grid_2.csv",
},
),
),
vm.Button(
id="button_2",
text="Export data",
actions=[vm.Action(function=ag_grid_data_exporting(), outputs=["underlying_ag_grid_2.exportDataAsCsv"])],
),
],
controls=[vm.Filter(column="continent"), vm.Filter(column="year")],
)
dashboard = vm.Dashboard(pages=[page_one, page_two])

dashboard = vm.Dashboard(pages=[page_one, page_two])

if __name__ == "__main__":
Vizro().build(dashboard).run()
2 changes: 1 addition & 1 deletion vizro-core/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ scripts = {lint = "SKIP=gitleaks pre-commit run {args:--all-files}"}
[envs.lower-bounds]
extra-dependencies = [
"pydantic==1.10.13",
"dash==2.17.0"
"dash==2.17.1"
]

[publish.index]
Expand Down
2 changes: 1 addition & 1 deletion vizro-core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ classifiers = [
"Programming Language :: Python :: 3.12"
]
dependencies = [
"dash>=2.17.0", # 2.17.0 needed for new features on loading spinner
"dash>=2.17.1", # 2.17.1 needed for no_output fix in clientside_callback
"dash_bootstrap_components",
"dash-ag-grid>=31.0.0",
"pandas",
Expand Down
2 changes: 1 addition & 1 deletion vizro-core/snyk/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dash>=2.17.0
dash>=2.17.1
dash_bootstrap_components
dash-ag-grid>=31.0.0
pandas
Expand Down
10 changes: 9 additions & 1 deletion vizro-core/src/vizro/models/_components/graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from typing import Dict, List, Literal

from dash import State, ctx, dcc
from dash import ClientsideFunction, Input, Output, State, clientside_callback, ctx, dcc
from dash.exceptions import MissingCallbackContextException
from plotly import graph_objects as go

Expand Down Expand Up @@ -116,6 +116,14 @@ def _filter_interaction(

@_log_call
def build(self):
clientside_callback(
ClientsideFunction(namespace="clientside", function_name="update_graph_theme"),
# Output here to ensure that the callback is only triggered if the graph exists on the currently open page.
output=[Output(self.id, "figure")],
inputs=[Input("theme_selector", "checked"), State("vizro_themes", "data"), State(self.id, "id")],
prevent_initial_call=True,
)

# The empty figure here is just a placeholder designed to be replaced by the actual figure when the filters
# etc. are applied. It only appears on the screen for a brief instant, but we need to make sure it's
# transparent and has no axes so it doesn't draw anything on the screen which would flicker away when the
Expand Down
16 changes: 14 additions & 2 deletions vizro-core/src/vizro/models/_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@
import dash
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
from dash import ClientsideFunction, Input, Output, State, clientside_callback, get_asset_url, get_relative_path, html
from dash import (
ClientsideFunction,
Input,
Output,
State,
clientside_callback,
dcc,
get_asset_url,
get_relative_path,
html,
)

try:
from pydantic.v1 import Field, validator
Expand All @@ -18,6 +28,7 @@
from dash.development.base_component import Component

import vizro
from vizro import _themes as themes
from vizro._constants import MODULE_PAGE_404, STATIC_URL_PREFIX
from vizro.actions._action_loop._action_loop import ActionLoop
from vizro.models import Navigation, VizroBaseModel
Expand Down Expand Up @@ -142,7 +153,8 @@ def build(self):
return html.Div(
id="dashboard-container",
children=[
html.Div(vizro.__version__, id="vizro_version", hidden=True),
html.Div(id="vizro_version", children=vizro.__version__, hidden=True),
dcc.Store(id="vizro_themes", data={"dark": themes.dark, "light": themes.light}),
ActionLoop._create_app_callbacks(),
dash.page_container,
],
Expand Down
32 changes: 1 addition & 31 deletions vizro-core/src/vizro/models/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import List, TypedDict

from dash import Input, Output, Patch, callback, dcc, html
from dash import dcc, html

try:
from pydantic.v1 import Field, root_validator, validator
Expand Down Expand Up @@ -108,7 +108,6 @@ def pre_build(self):

@_log_call
def build(self) -> _PageBuildType:
self._update_graph_theme()
controls_content = [control.build() for control in self.controls]
control_panel = html.Div(id="control-panel", children=controls_content, hidden=not controls_content)

Expand All @@ -120,32 +119,3 @@ def build(self) -> _PageBuildType:
components_container.children.append(dcc.Store(id=f"{ON_PAGE_LOAD_ACTION_PREFIX}_trigger_{self.id}"))
components_container.id = "page-components"
return html.Div([control_panel, components_container])

def _update_graph_theme(self):
# The obvious way to do this would be to alter pio.templates.default, but this changes global state and so is
# not good.
# Putting graphs as inputs here would be a nice way to trigger the theme change automatically so that we don't

# need the call to _update_theme inside Graph.__call__ also, but this results in an extra callback and the graph
# flickering.
# The code is written to be generic and extensible so that it runs _update_theme on any component with such a
# method defined. But at the moment this just means Graphs.
# TODO: consider making this clientside callback and then possibly we can remove the call to _update_theme in
# Graph.__call__ without any flickering.
# TODO: if we do this then we should *consider* defining the callback in Graph itself rather than at Page
# level. This would mean multiple callbacks on one page but if it's clientside that probably doesn't matter.

themed_components = [
model_manager[model_id]
for model_id in model_manager._get_model_children(model_id=ModelID(str(self.id)))
if hasattr(model_manager[model_id], "_update_theme")
]
if themed_components:

@callback(
[Output(component.id, "figure", allow_duplicate=True) for component in themed_components],
Input("theme_selector", "checked"),
prevent_initial_call="initial_duplicate",
)
def update_graph_theme(theme_selector: bool):
return [component._update_theme(Patch(), theme_selector) for component in themed_components]
8 changes: 5 additions & 3 deletions vizro-core/src/vizro/static/js/clientside_functions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {
_update_dashboard_theme,
_collapse_nav_panel,
_update_ag_grid_theme,
_update_graph_theme,
_collapse_nav_panel,
} from "./models/dashboard.js";
import { _update_range_slider_values } from "./models/range_slider.js";
import { _update_slider_values } from "./models/slider.js";
Expand All @@ -18,13 +19,14 @@ import {
window.dash_clientside = Object.assign({}, window.dash_clientside, {
clientside: {
update_dashboard_theme: _update_dashboard_theme,
update_ag_grid_theme: _update_ag_grid_theme,
update_graph_theme: _update_graph_theme,
collapse_nav_panel: _collapse_nav_panel,
update_range_slider_values: _update_range_slider_values,
update_slider_values: _update_slider_values,
trigger_to_global_store: _trigger_to_global_store,
gateway: _gateway,
after_action_cycle_breaker: _after_action_cycle_breaker,
collapse_nav_panel: _collapse_nav_panel,
update_ag_grid_theme: _update_ag_grid_theme,
update_date_picker_values: _update_date_picker_values,
update_date_picker_position: _update_date_picker_position,
},
Expand Down
15 changes: 15 additions & 0 deletions vizro-core/src/vizro/static/js/models/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ export function _update_ag_grid_theme(checked) {
: "ag-theme-quartz-dark ag-theme-vizro";
}

export function _update_graph_theme(checked, vizro_themes, graph_id) {
// Determine the theme to be applied based on the checked value
const theme_to_apply = checked ? vizro_themes["light"] : vizro_themes["dark"];

// Find the Plotly graph element in the HTML document
const plotly_graph = document
.getElementById(graph_id)
.querySelector(".js-plotly-plot");

// Adjust `layout` property for the Plotly graph element
Plotly.relayout(plotly_graph, { template: theme_to_apply });

return dash_clientside.no_update;
}

export function _collapse_nav_panel(n_clicks, is_open) {
if (!n_clicks) {
/* Automatically collapses left-side if xs and s-devices are detected*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ def test_failed_ag_grid_with_no_captured_callable(self, standard_go_chart):
with pytest.raises(ValidationError, match="must provide a valid CapturedCallable object"):
vm.AgGrid(figure=standard_go_chart)

@pytest.mark.xfail(reason="This test is failing as we are not yet detecting different types of captured callables")
def test_failed_ag_grid_with_wrong_captured_callable(self, standard_px_chart):
with pytest.raises(ValidationError, match="must provide a valid ag_grid function vm.AgGrid"):
with pytest.raises(ValidationError, match="CapturedCallable mode mismatch. Expected ag_grid but got graph."):
vm.AgGrid(figure=standard_px_chart)

def test_set_action_via_validator(self, standard_ag_grid, identity_action_function):
Expand Down
Loading

0 comments on commit 44b7810

Please sign in to comment.