diff --git a/vizro-core/changelog.d/20240607_125345_huong_li_nguyen_create_kpi_cards.md b/vizro-core/changelog.d/20240607_125345_huong_li_nguyen_create_kpi_cards.md index aeeb34aef..568c4f46a 100644 --- a/vizro-core/changelog.d/20240607_125345_huong_li_nguyen_create_kpi_cards.md +++ b/vizro-core/changelog.d/20240607_125345_huong_li_nguyen_create_kpi_cards.md @@ -6,8 +6,8 @@ Uncomment the section that is right (remove the HTML comment wrapper). ### Highlights ✨ -- Introduce `Figure` as a new `Page` component, enabling all dash components to be reactive in - `Vizro`. See the [user guide on figure](XXXX) for more information. ([#493](https://github.com/mckinsey/vizro/pull/493)) +- Introduce `Figure` as a new `Page` component, enabling all Dash components to be reactive in + `Vizro`. See the [user guide on figure](https://vizro.readthedocs.io/en/stable/pages/user-guides/figure/) for more information. ([#493](https://github.com/mckinsey/vizro/pull/493), [#524](https://github.com/mckinsey/vizro/pull/524)) - Introduce styled KPI cards to be used inside `Figure`. See the [user guide on KPI cards](XXXX) for more information. ([#493](https://github.com/mckinsey/vizro/pull/493)) + + + + + + + + diff --git a/vizro-core/docs/assets/user_guides/figure/custom_html.png b/vizro-core/docs/assets/user_guides/figure/custom_html.png new file mode 100644 index 000000000..82d4f8d48 Binary files /dev/null and b/vizro-core/docs/assets/user_guides/figure/custom_html.png differ diff --git a/vizro-core/docs/assets/user_guides/figure/custom_kpi.png b/vizro-core/docs/assets/user_guides/figure/custom_kpi.png new file mode 100644 index 000000000..b6d2f2727 Binary files /dev/null and b/vizro-core/docs/assets/user_guides/figure/custom_kpi.png differ diff --git a/vizro-core/docs/assets/user_guides/figure/custom_multiple_cards.png b/vizro-core/docs/assets/user_guides/figure/custom_multiple_cards.png new file mode 100644 index 000000000..3f1b66bf9 Binary files /dev/null and b/vizro-core/docs/assets/user_guides/figure/custom_multiple_cards.png differ diff --git a/vizro-core/docs/assets/user_guides/figure/figure.png b/vizro-core/docs/assets/user_guides/figure/figure.png new file mode 100644 index 000000000..dad982f3e Binary files /dev/null and b/vizro-core/docs/assets/user_guides/figure/figure.png differ diff --git a/vizro-core/docs/pages/API-reference/actions.md b/vizro-core/docs/pages/API-reference/action-callables.md similarity index 85% rename from vizro-core/docs/pages/API-reference/actions.md rename to vizro-core/docs/pages/API-reference/action-callables.md index 3cbd4d926..804dff6bc 100644 --- a/vizro-core/docs/pages/API-reference/actions.md +++ b/vizro-core/docs/pages/API-reference/action-callables.md @@ -1,4 +1,4 @@ -# Actions +# Action functions API reference for all pre-defined action functions. diff --git a/vizro-core/docs/pages/API-reference/captured-callables.md b/vizro-core/docs/pages/API-reference/captured-callables.md deleted file mode 100644 index a83e79850..000000000 --- a/vizro-core/docs/pages/API-reference/captured-callables.md +++ /dev/null @@ -1,21 +0,0 @@ - -# Table functions - -API reference for all pre-defined [`CapturedCallable`][vizro.models.types.CapturedCallable] table functions to be used in the -[`AgGrid`][vizro.models.AgGrid] and [`Table`][vizro.models.Table] models. Visit the how-to guide on [tables](../user-guides/table.md) -for more information. - -::: vizro.tables - options: - show_source: true - -# Figure functions -API reference for all pre-defined [`CapturedCallable`][vizro.models.types.CapturedCallable] figure functions to be used in the -[`Figure`][vizro.models.Figure]. Visit the how-to guide on [figures](../user-guides/figure.md) -for more information. - -::: vizro.figures - options: - show_source: true - - diff --git a/vizro-core/docs/pages/API-reference/figure-callables.md b/vizro-core/docs/pages/API-reference/figure-callables.md new file mode 100644 index 000000000..ada2b5d95 --- /dev/null +++ b/vizro-core/docs/pages/API-reference/figure-callables.md @@ -0,0 +1,10 @@ + +# Figure functions +API reference for all pre-defined [`CapturedCallable`][vizro.models.types.CapturedCallable] figure functions to be used in the +[`Figure`][vizro.models.Figure]. The [how-to guide on figures](../user-guides/figure.md) contains more information. + +::: vizro.figures + options: + show_source: true + + diff --git a/vizro-core/docs/pages/API-reference/table-callables.md b/vizro-core/docs/pages/API-reference/table-callables.md new file mode 100644 index 000000000..7c3c8d33c --- /dev/null +++ b/vizro-core/docs/pages/API-reference/table-callables.md @@ -0,0 +1,11 @@ + +# Table functions + +API reference for all pre-defined [`CapturedCallable`][vizro.models.types.CapturedCallable] table functions to be used in the +[`AgGrid`][vizro.models.AgGrid] and [`Table`][vizro.models.Table] models. The how-to guide on [tables](../user-guides/table.md) contains more information. + +::: vizro.tables + options: + show_source: true + + diff --git a/vizro-core/docs/pages/user-guides/components.md b/vizro-core/docs/pages/user-guides/components.md index 222ffbff1..106bf6a0b 100755 --- a/vizro-core/docs/pages/user-guides/components.md +++ b/vizro-core/docs/pages/user-guides/components.md @@ -6,7 +6,7 @@ listed below to fill your dashboard with visuals.
-- :octicons-graph-16:{ .lg .middle } __Graph__ +- :octicons-graph-16:{ .lg .middle } __Graphs__ --- @@ -14,7 +14,7 @@ listed below to fill your dashboard with visuals. [:octicons-arrow-right-24: View user guide](graph.md) -- :material-table-large:{ .lg .middle } __Table__ +- :material-table-large:{ .lg .middle } __Tables__ --- @@ -22,23 +22,24 @@ listed below to fill your dashboard with visuals. [:octicons-arrow-right-24: View user guide](table.md) -- :material-cards-outline:{ .lg .middle } __Card & button__ +- :material-graph:{ .lg .middle } __Figures__ --- - Use cards and buttons to visualize text, navigate to different URLs or attach any [action](actions.md). + Use figures to make any [Dash component](https://dash.plotly.com/#open-source-component-libraries) reactive. - [:octicons-arrow-right-24: View user guide](card-button.md) + [:octicons-arrow-right-24: View user guide](figure.md) -- :material-graph:{ .lg .middle } __Figure__ +- :material-cards-outline:{ .lg .middle } __Cards & buttons__ --- - Use figure to visualize your make any dash component reactive. + Use cards and buttons to visualize text, navigate to different URLs or attach any [action](actions.md). + + [:octicons-arrow-right-24: View user guide](card-button.md) - [:octicons-arrow-right-24: View user guide](figure.md) -- :octicons-table-16:{ .lg .middle } __Container__ +- :octicons-table-16:{ .lg .middle } __Containers__ --- diff --git a/vizro-core/docs/pages/user-guides/custom-charts.md b/vizro-core/docs/pages/user-guides/custom-charts.md index 31a312ecb..e5c7eebca 100644 --- a/vizro-core/docs/pages/user-guides/custom-charts.md +++ b/vizro-core/docs/pages/user-guides/custom-charts.md @@ -3,26 +3,27 @@ This guide shows you how to create custom charts and how to add them to your dashboard. The [`Graph`][vizro.models.Graph] model accepts the `figure` argument, where you can enter _any_ [`plotly.express`](https://plotly.com/python/plotly-express/) chart as explained in the [user guide on graphs](graph.md). -## Overview of custom charts +## When to use a custom chart +In general, you should use the custom chart decorator `@capture("graph")` if your plotly chart needs any post-update calls or customization. For example: -In general, the usage of the custom chart decorator `@capture("graph")` is required if your plotly chart requires any post-update calls or customization. +- You want to use any of the post figure update calls by `plotly` such as `update_layout`, `update_xaxes`, `update_traces` (for more details, see the docs on [plotly's update calls](https://plotly.com/python/creating-and-updating-figures/#other-update-methods)) +- You want to use a custom-created [`plotly.graph_objects.Figure()`](https://plotly.com/python/graph-objects/) object (in short, `go.Figure()`) and add traces yourself via [`add_trace`](https://plotly.com/python/creating-and-updating-figures/#adding-traces) -### When to use a custom chart +## Steps to create a custom chart -- If you want to use any of the post figure update calls by `plotly` such as `update_layout`, `update_xaxes`, `update_traces` (for more details, see the docs on [plotly's update calls](https://plotly.com/python/creating-and-updating-figures/#other-update-methods)) -- If you want to use a custom-created [`plotly.graph_objects.Figure()`](https://plotly.com/python/graph-objects/) object (in short, `go.Figure()`) and add traces yourself via [`add_trace`](https://plotly.com/python/creating-and-updating-figures/#adding-traces) +1. Define a function that returns a `go.Figure()`. +2. Decorate it with `@capture("graph")`. +3. The function must accept a `data_frame` argument (of type `pandas.DataFrame`). +4. The visualization should be derived from and require only one `pandas.DataFrame`. Dataframes from other arguments +will not react to dashboard controls such as [`Filter`](filters.md). +5. Pass your function to the `figure` argument of the [`Graph`][vizro.models.Graph] model. -### Requirements of a custom chart function - -- a `go.Figure()` object is returned by the function -- the function must be decorated with the `@capture("graph")` decorator -- the function accepts a `data_frame` argument (of type `pandas.DataFrame`) -- the visualization is derived from and requires only one `pandas.DataFrame` (for example, any further dataframes added through other arguments will not react to dashboard components such as `Filter`) - -The below minimal example can be used as a base to build more sophisticated charts. +The minimal example below can be used as a base to build more sophisticated charts. ```py title="Minimal example of a custom chart" from vizro.models.types import capture +import pandas as pd +import plotly.graph_objects as go @capture("graph") def minimal_example(data_frame:pd.DataFrame=None): diff --git a/vizro-core/docs/pages/user-guides/custom-components.md b/vizro-core/docs/pages/user-guides/custom-components.md index 121209040..94997230c 100644 --- a/vizro-core/docs/pages/user-guides/custom-components.md +++ b/vizro-core/docs/pages/user-guides/custom-components.md @@ -11,7 +11,7 @@ In general, you can create a custom component based on any dash-compatible compo All our components are based on `Dash`, and they are shipped with a set of sensible defaults that can be modified. If you would like to overwrite one of those defaults, -or if you would like to use extra `args` or `kwargs` of those components, then this is the correct way to include those. You can use any existing attribute of any underlying Dash component with this method. +or if you would like to use extra `args` or `kwargs` of those components, then this is the correct way to include those. You can use any existing attribute of any underlying [Dash component](https://dash.plotly.com/#open-source-component-libraries) with this method. !!!note diff --git a/vizro-core/docs/pages/user-guides/custom-figures.md b/vizro-core/docs/pages/user-guides/custom-figures.md new file mode 100644 index 000000000..cf78c61f7 --- /dev/null +++ b/vizro-core/docs/pages/user-guides/custom-figures.md @@ -0,0 +1,280 @@ +# How to create custom figures + +This guide explains how to create custom figures, which is useful when you need a component that reacts to +[filter](filters.md) and [parameter](parameters.md) controls. + +The [`Figure`][vizro.models.Figure] model accepts the `figure` argument, where you can enter _any_ custom figure function +as explained in the [user guide on figures](figure.md). + +## When to use a custom figure +As described in the flowchart detailing [when to use `Figure`](figure.md), custom figures should be used if +**both** of the following conditions are met: + +- You need a figure that doesn't fit into the existing pre-defined components ([`Graph`][vizro.models.Graph], [`Table`][vizro.models.Table] or [`AgGrid`][vizro.models.AgGrid]). +- You need a figure that isn't available in our pre-defined figure functions [`vizro.figures`](../API-reference/figure-callables.md). + +## Steps to create a custom figure + +1. Define a function that returns a [Dash component](https://dash.plotly.com/#open-source-component-libraries). +This can, but does not need to, be based on code in our pre-defined figure functions in [`vizro.figures`](../API-reference/figure-callables.md). +2. Decorate it with `@capture("figure")`. +3. The function must accept a `data_frame` argument (of type `pandas.DataFrame`). +4. The figure should be derived from and require only one `pandas.DataFrame`. Dataframes from other arguments +will not react to dashboard controls such as [`Filter`](filters.md). +5. Pass your function to the `figure` argument of the [`Figure`][vizro.models.Figure] model. + +The following examples can be used as a base to build more sophisticated figures. + +## Examples of custom figures + +### Custom KPI card +If you wish to change the design or content of our existing KPI (key performance indicator) cards from +[`vizro.figures`](../API-reference/figure-callables.md), you can do so by following the steps described above. + +For instance, to make a KPI card with the icon positioned on the right side of the title instead of the left, +copy and paste the [source code of `kpi_card`](../API-reference/figure-callables.md/#kpi_card) and +adjust the return statement of the function. + + +!!! example "Custom KPI card" + === "app.py" + ```py + from typing import Optional + + import dash_bootstrap_components as dbc + import pandas as pd + import vizro.models as vm + import vizro.plotly.express as px + from dash import html + from vizro import Vizro + from vizro.figures import kpi_card + from vizro.models.types import capture + + tips = px.data.tips + + + @capture("figure") # (1)! + def custom_kpi_card( + data_frame: pd.DataFrame, + value_column: str, + *, + value_format: str = "{value}", + agg_func: str = "sum", + title: Optional[str] = None, + icon: Optional[str] = None, + ) -> dbc.Card: # (2)! + """Creates a custom KPI Card.""" + title = title or f"{agg_func} {value_column}".title() + value = data_frame[value_column].agg(agg_func) + + return dbc.Card( + [ + dbc.CardHeader( + [ + html.H2(title), + html.P(icon, className="material-symbols-outlined") if icon else None, # (3)! + ], + ), + dbc.CardBody([value_format.format(value=value)]), + ], + className="card-kpi", + ) + + + page = vm.Page( + title="Create your own KPI Card", + layout=vm.Layout(grid=[[0, 1, -1, -1]] + [[-1, -1, -1, -1]] * 3), # (4)! + components=[ + vm.Figure( + figure=kpi_card( # (5)! + data_frame=tips, + value_column="tip", + value_format="${value:.2f}", + icon="shopping_cart", + title="Default KPI Card", + ) + ), + vm.Figure( + figure=custom_kpi_card( # (6)! + data_frame=tips, + value_column="tip", + value_format="${value:.2f}", + icon="payment", + title="Custom KPI Card", + ) + ), + ], + ) + + dashboard = vm.Dashboard(pages=[page]) + Vizro().build(dashboard).run() + ``` + + 1. Here we decorate our custom figure function with the `@capture("figure")` decorator. + 2. The custom figure function needs to have a `data_frame` argument and return a `Dash` component. + 3. We adjust the return statement to include the icon on the right side of the title. This is achieved by swapping the order of the `html.H2` and `html.P` compared to the original `kpi_card`. + 4. This creates a [`layout`](layouts.md) with four rows and columns. The KPI cards are positioned in the first two cells, while the remaining cells are empty. + 5. For more information, refer to the API reference for the [`kpi_card`](../API-reference/figure-callables.md#kpi_card). + 6. Our custom figure function `custom_kpi_card` now needs to be passed on to the `vm.Figure`. + + === "app.yaml" + ```yaml + # Custom figures are currently only possible via python configuration + ``` + === "Result" + [![CustomKPI]][CustomKPI] + + [CustomKPI]: ../../assets/user_guides/figure/custom_kpi.png + + + +### Dynamic HTML header +Generally, you can create a custom figure for any [Dash component](https://dash.plotly.com/#open-source-component-libraries). +Below is an example of a custom figure that returns a `html.H2` component that dynamically updates based on the selected +name from a filter. + + +!!! example "Dynamic HTML header" + === "app.py" + ```py + import pandas as pd + import vizro.models as vm + from dash import html + from vizro import Vizro + from vizro.models.types import capture + + df = pd.DataFrame({"names": ["Emma", "Jack", "Sophia", "Ethan", "Mia"]}) + + + @capture("figure") # (1)! + def dynamic_html_header(data_frame: pd.DataFrame, column: str) -> html.H2: # (2)! + """Creates a HTML header that dynamically updates based on controls.""" + return html.H2(f"Good morning, {data_frame[column].iloc[0]}! ☕ ⛅") # (3)! + + + page = vm.Page( + title="Dynamic HTML header", + components=[vm.Figure(figure=dynamic_html_header(data_frame=df, column="names"))], # (4)! + controls=[vm.Filter(column="names", selector=vm.RadioItems(title="Select a name"))], + ) + + dashboard = vm.Dashboard(pages=[page]) + Vizro().build(dashboard).run() + ``` + + 1. Here we decorate our custom figure function with the `@capture("figure")` decorator. + 2. The custom figure function needs to have a `data_frame` argument and return a `Dash` component. + 3. We return a `html.H2` component that dynamically updates based on the selected name from the filter. + 4. Our custom figure function `dynamic_html_header` now needs to be passed on to the `vm.Figure`. + + === "app.yaml" + ```yaml + # Custom figures are currently only possible via python configuration + ``` + === "Result" + [![CustomHTML]][CustomHTML] + + [CustomHTML]: ../../assets/user_guides/figure/custom_html.png + + + + +### Dynamic number of cards +The example below shows how to create multiple cards created from a `pandas.DataFrame` where the +number of cards displayed dynamically adjusts based on a `vm.Parameter`. + + +!!! example "Dynamic number of cards" + === "app.py" + ```py + from typing import Optional + + import dash_bootstrap_components as dbc + import pandas as pd + import vizro.models as vm + from dash import dcc, html + from vizro import Vizro + from vizro.models.types import capture + + text = [ + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea elitr sed diam nonumy.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + "Sed diam voluptua. At vero eos et accusam et justo no duo dolores et ea rebum.", + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea est elitr dolor sit amet.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + ] + + df = pd.DataFrame({"text": text * 2}) + + + @capture("figure") # (1)! + def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.Div: # (2)! + """Creates a list with a variable number of `vm.Card` components from the provided data_frame. + + Args: + data_frame: Data frame containing the data. + n_rows: Number of rows to use from the data_frame. Defaults to 1. + + Returns: + html.Div with a list of dbc.Card objects generated from the data. + + """ + texts = data_frame.head(n_rows)["text"] + return html.Div( + [dbc.Card(dcc.Markdown(f"### Card #{i}\n{text}")) for i, text in enumerate(texts, 1)], + className="multiple-cards-container", + ) + + + page = vm.Page( + title="Page with variable number of cards", + components=[vm.Figure(id="my-figure", figure=multiple_cards(data_frame=df))], # (3)! + controls=[ + vm.Parameter( + targets=["my-figure.n_rows"], # (4)! + selector=vm.Slider(min=2, max=12, step=2, value=8, title="Number of cards to display"), + ), + ], + ) + + dashboard = vm.Dashboard(pages=[page]) + Vizro().build(dashboard).run() + ``` + + 1. Here we decorate our custom figure function with the `@capture("figure")` decorator. + 2. The custom figure function needs to have a `data_frame` argument and return a `Dash` component. + 3. Our decorated figure function `multiple_cards` now needs to be passed on to the `vm.Figure`. + 4. We add a [`vm.Parameter`](parameters.md) to dynamically adjust the number of cards displayed. + The parameter targets the `n_rows` argument of the `multiple_cards` function, determining the number of rows + taken from the data. + + === "css" + ```css + .multiple-cards-container { + display: flex; + flex-wrap: wrap; + gap: 12px; + } + + .figure-container { + height: unset; + width: unset; + } + + .figure-container .card { + height: 210px; + width: 240px; + } + ``` + === "app.yaml" + ```yaml + # Custom figures are currently only possible via python configuration + ``` + === "Result" + [![CustomFigure]][CustomFigure] + + [CustomFigure]: ../../assets/user_guides/figure/custom_multiple_cards.png + + + diff --git a/vizro-core/docs/pages/user-guides/custom-tables.md b/vizro-core/docs/pages/user-guides/custom-tables.md index 9a50816a5..e5fa4d413 100644 --- a/vizro-core/docs/pages/user-guides/custom-tables.md +++ b/vizro-core/docs/pages/user-guides/custom-tables.md @@ -3,14 +3,19 @@ In cases where the available arguments for the [`dash_ag_grid`][vizro.tables.dash_ag_grid] or [`dash_data_table`][vizro.tables.dash_data_table] models are not sufficient, you can create a custom Dash AG Grid or Dash DataTable. +The [`Table`][vizro.models.Table] and the [`AgGrid`][vizro.models.AgGrid] model accept the `figure` argument, where you can enter +_any_ [`dash_ag_grid`][vizro.tables.dash_ag_grid] or [`dash_data_table`][vizro.tables.dash_data_table] chart as explained in the [user guide on tables](table.md). + One reason could be that you want to create a table/grid that requires computations that can be controlled by parameters (see the example below). -For this, similar to how one would create a [custom chart](../user-guides/custom-charts.md), do the following: +### Steps to create a custom table -- Define a function that returns a `dash_ag_grid.AgGrid` or `dash_table.DataTable` object. -- Decorate it with the `@capture("ag_grid")` or `@capture("table")` decorator respectively. -- The function must accept a `data_frame` argument (of type `pandas.DataFrame`). -- The table should be derived from and require only one `pandas.DataFrame` (for example, any further dataframes added through other arguments will not react to dashboard components such as `Filter`). +1. Define a function that returns a `dash_ag_grid.AgGrid` or `dash_table.DataTable` object. +2. Decorate it with `@capture("ag_grid")` or `@capture("table")`. +3. The function must accept a `data_frame` argument (of type `pandas.DataFrame`). +4. The table should be derived from and require only one `pandas.DataFrame`. Dataframes from other arguments +will not react to dashboard controls such as [`Filter`](filters.md). +5. Pass your function to the `figure` argument of the [`Table`][vizro.models.Table] or [`AgGrid`][vizro.models.AgGrid] model. The following examples show a possible version of a custom table. In this case the argument `chosen_columns` was added, which you can control with a parameter: diff --git a/vizro-core/docs/pages/user-guides/figure.md b/vizro-core/docs/pages/user-guides/figure.md index 1d8a9a8d2..c9c596d50 100644 --- a/vizro-core/docs/pages/user-guides/figure.md +++ b/vizro-core/docs/pages/user-guides/figure.md @@ -1 +1,120 @@ # How to use figures + +This guide shows you how to add any [Dash component](https://dash.plotly.com/#open-source-component-libraries) that needs to be reactive to [filter](filters.md) and [parameter](parameters.md) controls. +If you want to add a static Dash component to your page, use [custom components](custom-components.md) instead. + +[`Figure`][vizro.models.Figure] provides a flexible foundation for all types of reactive Dash components in Vizro. +The [`Graph`][vizro.models.Graph], [`Table`][vizro.models.Table] and [`AgGrid`][vizro.models.AgGrid] components are +specific implementations of `Figure`. They serve as intuitive shortcuts, embedding behaviors and interactions specific +to their purposes. + +If these more specific models already achieve what you need then they should be used in preference to +the more generic `Figure`. Remember that it is possible to supply [custom charts](custom-charts.md) to `Graph` +and [custom tables](custom-tables.md) to `Table`. + +There are already a few figure functions you can reuse: + +- [`kpi_card`][vizro.figures.kpi_card] +- [`kpi_card_reference`][vizro.figures.kpi_card_reference] + +The following flowchart shows what you need to consider when choosing which model to use: + +``` mermaid +graph TD + first["`Does your desired component exist in Vizro, e.g. Graph, Table or AgGrid?`"] + specific-component([Use the specific component]) + second["`Does your component need to be reactive to controls?`"] + second-static([Use custom components]) + second-reactive([Use Figure]) + + first -- Yes --> specific-component + first -- No --> second + second -- No --> second-static + second -- Yes --> second-reactive + + click specific-component href "../components/" + click second-static href "../custom-components/" + click second-reactive href "#how-to-use-figures" + + classDef clickable color:#4051b5; +``` + + +To add a `Figure` to your page: + +1. Add the `Figure` model to the components argument of the [Page][vizro.models.Page] model. +2. Use an existing figure function from [`vizro.figures`](../API-reference/figure-callables.md) and pass it to the `figure` argument of the `Figure` model. + +!!! example "Use existing figure functions" + + === "app.py" + ```py + import vizro.models as vm + import vizro.plotly.express as px + from vizro import Vizro + from vizro.figures import kpi_card + + tips = px.data.tips + + page = vm.Page( + title="KPI Indicators", + layout=vm.Layout(grid=[[0, -1, -1, -1]] + [[-1, -1, -1, -1]] * 4), # (1)! + components=[ + vm.Figure( + figure=kpi_card( # (2)! + data_frame=tips, + value_column="tip", + value_format="${value:.2f}", + icon="shopping_cart", + title="KPI Card I", + ) + ) + ], + controls=[vm.Filter(column="day", selector=vm.RadioItems())], + ) + + dashboard = vm.Dashboard(pages=[page]) + Vizro().build(dashboard).run() + ``` + + 1. This creates a [`layout`](layouts.md) with five rows and four columns. The KPI card is positioned in the first cell, while the remaining cells are empty. + 2. For more information, refer to the API reference for the [`kpi_card`](../API-reference/figure-callables.md#kpi_card). + + === "app.yaml" + ```yaml + # Still requires a .py to add data to the data manager and parse YAML configuration + # See from_yaml example + pages: + - components: + - figure: + _target_: kpi_card + data_frame: tips + value_column: tip + value_format: ${value:.2f} + icon: shopping_cart + title: KPI Card I + type: figure + controls: + - column: day + type: filter + selector: + type: radio_items + layout: + grid: + [ + [0, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + ] + title: KPI Indicators + ``` + === "Result" + [![Figure]][Figure] + + [Figure]: ../../assets/user_guides/figure/figure.png + + +If the pre-defined figure functions from [`vizro.figures`](../API-reference/figure-callables.md) do not serve your purpose, +there is always the option to create a [custom figure](custom-figures.md). diff --git a/vizro-core/examples/_dev/app.py b/vizro-core/examples/_dev/app.py index a1699fe5f..9b783413a 100644 --- a/vizro-core/examples/_dev/app.py +++ b/vizro-core/examples/_dev/app.py @@ -1,23 +1,57 @@ -"""Example to show dashboard configuration.""" +"""Dev app to try things out.""" +from typing import Optional + +import dash_bootstrap_components as dbc +import pandas as pd import vizro.models as vm -import vizro.plotly.express as px +from dash import dcc, html from vizro import Vizro +from vizro.models.types import capture -df = px.data.gapminder() +text = [ + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea elitr sed diam nonumy.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + "Sed diam voluptua. At vero eos et accusam et justo no duo dolores et ea rebum.", + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea est elitr dolor sit amet.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", +] -page_one = vm.Page( - title="Vizro filters exporting", - components=[ - vm.Graph(id="graph_1", figure=px.scatter(df, x="gdpPercap", y="lifeExp", color="continent", size="pop")), - vm.Graph(id="graph_2", figure=px.scatter(df, x="gdpPercap", y="lifeExp", color="continent", size="pop")), - ], +df = pd.DataFrame({"text": text * 2}) + + +@capture("figure") # (1)! +def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.Div: # (2)! + """Creates a list with a variable number of `vm.Card` components from the provided data_frame. + + Args: + data_frame: Data frame containing the data. + n_rows: Number of rows to use from the data_frame. Defaults to 1. + + Returns: + html.Div with a list of dbc.Card objects generated from the data. + + """ + texts = data_frame.head(n_rows)["text"] + return html.Div( + [dbc.Card(dcc.Markdown(f"### Card #{i}\n{text}")) for i, text in enumerate(texts, 1)], + className="multiple-cards-container", + ) + + +page = vm.Page( + title="Page with variable number of cards", + components=[vm.Figure(id="my-figure", figure=multiple_cards(data_frame=df))], # (3)! controls=[ - vm.Filter(column="continent"), + vm.Parameter( + targets=["my-figure.n_rows"], # (4)! + selector=vm.Slider(min=2, max=12, step=2, value=8, title="Number of cards to display"), + ), ], ) -dashboard = vm.Dashboard(pages=[page_one]) +dashboard = vm.Dashboard(pages=[page]) if __name__ == "__main__": Vizro().build(dashboard).run() diff --git a/vizro-core/examples/_dev/assets/css/custom.css b/vizro-core/examples/_dev/assets/css/custom.css index 8a2651c0b..4d79dddef 100644 --- a/vizro-core/examples/_dev/assets/css/custom.css +++ b/vizro-core/examples/_dev/assets/css/custom.css @@ -1,4 +1,5 @@ -/* Apply reverse color logic via CSS */ -#kpi-card-reverse-coloring .card-kpi:has(.color-neg) { - border-left: 4px solid #1a85ff; +.multiple-cards-container { + display: flex; + flex-wrap: wrap; + gap: 12px; } diff --git a/vizro-core/examples/_dev/yaml_version/app.py b/vizro-core/examples/_dev/yaml_version/app.py index 808cdae9b..9f8d84516 100644 --- a/vizro-core/examples/_dev/yaml_version/app.py +++ b/vizro-core/examples/_dev/yaml_version/app.py @@ -12,7 +12,7 @@ data_manager["iris"] = px.data.iris() data_manager["gapminder"] = px.data.gapminder() data_manager["gapminder_2007"] = px.data.gapminder().query("year == 2007") - +data_manager["tips"] = px.data.tips() df_stocks_long = pd.melt( px.data.stocks(datetimes=True), diff --git a/vizro-core/examples/_dev/yaml_version/dashboard.yaml b/vizro-core/examples/_dev/yaml_version/dashboard.yaml index cd3cb3923..7e14655db 100644 --- a/vizro-core/examples/_dev/yaml_version/dashboard.yaml +++ b/vizro-core/examples/_dev/yaml_version/dashboard.yaml @@ -3,10 +3,25 @@ pages: - components: - figure: - _target_: dash_ag_grid - data_frame: gapminder - dashGridOptions: - pagination: true - title: Dash AG Grid - type: ag_grid - title: Example of a Dash AG Grid + _target_: kpi_card + data_frame: tips + value_column: tip + value_format: ${value:.2f} + icon: shopping_cart + title: KPI Card I + type: figure + controls: + - column: day + type: filter + selector: + type: radio_items + layout: + grid: + [ + [0, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + ] + title: KPI Indicators diff --git a/vizro-core/examples/features/app.py b/vizro-core/examples/features/app.py index 0155eb348..90464e542 100644 --- a/vizro-core/examples/features/app.py +++ b/vizro-core/examples/features/app.py @@ -3,13 +3,15 @@ from time import sleep from typing import List, Literal, Optional +import dash_bootstrap_components as dbc import pandas as pd import plotly.graph_objects as go import vizro.models as vm import vizro.plotly.express as px -from dash import dash_table, html +from dash import dash_table, dcc, html from vizro import Vizro from vizro.actions import export_data, filter_interaction +from vizro.figures import kpi_card from vizro.models.types import capture from vizro.tables import dash_ag_grid, dash_data_table @@ -25,6 +27,20 @@ "y": [60, 80, 0, -40, -20, 0], } ) +custom_fig_df = pd.DataFrame( + { + "text": [ + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea elitr sed diam nonumy.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + "Sed diam voluptua. At vero eos et accusam et justo no duo dolores et ea rebum.", + "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", + "Lorem ipsum dolor sit amet, consetetur sadipscing no sea est elitr dolor sit amet.", + "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", + ] + * 2 + } +) + # HOME ------------------------------------------------------------------------ home = vm.Page( @@ -37,7 +53,7 @@ ### Components - Main components of Vizro include **charts**, **tables**, **cards**, **containers**, + Main components of Vizro include **charts**, **tables**, **cards**, **figures**, **containers**, **buttons** and **tabs**. """, href="/graphs", @@ -180,6 +196,23 @@ ], ) +figure = vm.Page( + title="Figure", + layout=vm.Layout(grid=[[0, -1, -1, -1]] + [[-1, -1, -1, -1]] * 4), + components=[ + vm.Figure( + figure=kpi_card( + data_frame=tips, + value_column="tip", + value_format="${value:.2f}", + icon="shopping_cart", + title="KPI Card I", + ) + ) + ], + controls=[vm.Filter(column="day", selector=vm.RadioItems())], +) + button = vm.Page( title="Button", layout=vm.Layout(grid=[[0], [0], [0], [0], [1]]), @@ -653,11 +686,42 @@ def my_custom_action(t: int): controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))], ) + +# CUSTOM FIGURE ---------------------------------------------------------------- +@capture("figure") # (1)! +def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.Div: + """Creates a list with a variable number of `vm.Card` components from the provided data_frame. + + Args: + data_frame: Data frame containing the data. + n_rows: Number of rows to use from the data_frame. Defaults to 1. + + Returns: + html.Div with a list of dbc.Card objects generated from the data. + + """ + texts = data_frame.head(n_rows)["text"] + return html.Div( + [dbc.Card(dcc.Markdown(f"### Card #{i}\n{text}")) for i, text in enumerate(texts, 1)], + className="multiple-cards-container", + ) + + +custom_figures = vm.Page( + title="Custom Figures", + components=[vm.Figure(id="my-figure", figure=multiple_cards(data_frame=custom_fig_df))], + controls=[ + vm.Parameter( + targets=["my-figure.n_rows"], + selector=vm.Slider(min=2, max=12, step=2, value=8, title="Number of cards to display"), + ), + ], +) # DASHBOARD ------------------------------------------------------------------- -components = [graphs, ag_grid, table, cards, button, containers, tabs] +components = [graphs, ag_grid, table, cards, figure, button, containers, tabs] controls = [filters, parameters, selectors] actions = [export_data_action, chart_interaction] -extensions = [custom_charts, custom_tables, custom_components, custom_actions] +extensions = [custom_charts, custom_tables, custom_components, custom_actions, custom_figures] dashboard = vm.Dashboard( title="Vizro Features", @@ -669,10 +733,16 @@ def my_custom_action(t: int): vm.NavLink( label="Features", pages={ - "Components": ["Graphs", "AG Grid", "Table", "Cards", "Button", "Containers", "Tabs"], + "Components": ["Graphs", "AG Grid", "Table", "Cards", "Figure", "Button", "Containers", "Tabs"], "Controls": ["Filters", "Parameters", "Selectors"], "Actions": ["Export data", "Chart interaction"], - "Extensions": ["Custom Charts", "Custom Tables", "Custom Components", "Custom Actions"], + "Extensions": [ + "Custom Charts", + "Custom Tables", + "Custom Components", + "Custom Actions", + "Custom Figures", + ], }, icon="Library Add", ), diff --git a/vizro-core/examples/features/assets/css/custom.css b/vizro-core/examples/features/assets/css/custom.css index f8c8df785..6de1c7d59 100644 --- a/vizro-core/examples/features/assets/css/custom.css +++ b/vizro-core/examples/features/assets/css/custom.css @@ -1,3 +1,19 @@ #page-header { padding-left: 8px; } + +.multiple-cards-container { + display: flex; + flex-wrap: wrap; + gap: 12px; +} + +.figure-container { + height: unset; + width: unset; +} + +.figure-container .card { + height: 210px; + width: 240px; +} diff --git a/vizro-core/examples/features/yaml_version/dashboard.yaml b/vizro-core/examples/features/yaml_version/dashboard.yaml index 2b25679da..82f9a3447 100644 --- a/vizro-core/examples/features/yaml_version/dashboard.yaml +++ b/vizro-core/examples/features/yaml_version/dashboard.yaml @@ -5,7 +5,7 @@ pages: ### Components - Main components of vizro include **charts**, **tables**, **cards**, **containers**, **buttons** and **tabs**. + Main components of vizro include **charts**, **tables**, **cards**, **figures**, **containers**, **buttons** and **tabs**. href: /graphs type: card - text: | @@ -130,6 +130,30 @@ pages: This word will be _**bold and italic**_ type: card title: Cards + - components: + - figure: + _target_: kpi_card + data_frame: tips + value_column: tip + value_format: ${value:.2f} + icon: shopping_cart + title: KPI Card I + type: figure + controls: + - column: day + type: filter + selector: + type: radio_items + layout: + grid: + [ + [0, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + [-1, -1, -1, -1], + ] + title: Figure - components: - figure: _target_: scatter @@ -286,6 +310,11 @@ pages: ## Custom actions are currently only possible via python configuration type: card title: Custom Actions + - components: + - text: | + ## Custom figures are currently only possible via python configuration + type: card + title: Custom Figures - components: - components: - figure: @@ -481,6 +510,7 @@ navigation: - AG Grid - Table - Cards + - Figure - Button - Containers - Tabs @@ -496,4 +526,5 @@ navigation: - Custom Tables - Custom Components - Custom Actions + - Custom Figures title: Vizro Features diff --git a/vizro-core/mkdocs.yml b/vizro-core/mkdocs.yml index 591eac13e..d7be9a0de 100644 --- a/vizro-core/mkdocs.yml +++ b/vizro-core/mkdocs.yml @@ -39,11 +39,13 @@ nav: - Custom tables: pages/user-guides/custom-tables.md - Custom components: pages/user-guides/custom-components.md - Custom actions: pages/user-guides/custom-actions.md + - Custom figures: pages/user-guides/custom-figures.md - API Reference: - Vizro: pages/API-reference/vizro.md - Models: pages/API-reference/models.md - - Actions: pages/API-reference/actions.md - - Table functions: pages/API-reference/captured-callables.md + - Action functions: pages/API-reference/action-callables.md + - Table functions: pages/API-reference/table-callables.md + - Figure functions: pages/API-reference/figure-callables.md - Explanation: - FAQs: pages/explanation/faq.md - Contribute to Vizro: pages/explanation/contributing.md