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

[Bug] Fix text alignment and enable href in Button #881

Merged
merged 19 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
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

- Enable `href` inside `vm.Button`. ([#881](https://github.com/mckinsey/vizro/pull/881))

<!--
### 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))

-->
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 63 additions & 10 deletions vizro-core/docs/pages/user-guides/card-button.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,12 @@ img[src*="#my-image"] {

### Create a navigation card
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For review for @stichbury


This section describes how to use the [`Card`][vizro.models.Card] component to create a navigation card. To configure the navigation panel on the left hand side of the screen, refer to the [guide on navigation](navigation.md).
This section describes how to use the [`Card`][vizro.models.Card] component to create a navigation card,
enabling users to navigate to another page by clicking on the card area.

A navigation card enables you to navigate to a different page via a click on the card area.
For a button-style link navigation component, see the [separate guide on creating a link button](#create-a-link-button).
To configure the navigation panel on the left hand side of the screen, refer to the
[separate guide on navigation](navigation.md).

To create a navigation card:

Expand Down Expand Up @@ -587,21 +590,70 @@ For detailed examples on how to create a KPI card, refer to the [figure user gui

## Buttons

To enhance dashboard interactions, you can use the [`Button`][vizro.models.Button] component to trigger any pre-defined
action functions such as exporting chart data. To use the currently available options for the [`Actions`][vizro.models.Action]
component, check out the [API reference][vizro.actions].
The Button component is commonly used for interactive dashboard interactions
such as form submissions, navigation links, and other action triggers.

To add a [`Button`][vizro.models.Button], insert it into the `components` argument of the
[`Page`][vizro.models.Page].

You can configure the `text` argument to alter the display text of the [`Button`][vizro.models.Button] and the
`actions` argument to define which action function should be executed on button click.

In the below example we show how to configure a button to export the filtered data of a target chart using
[export_data][vizro.actions.export_data], a pre-defined action function.
### Customize button text

You can configure the `text` argument to alter the display text of the [`Button`][vizro.models.Button].

!!! example "Button"
!!! example "Customize text"

=== "app.py"
```{.python pycafe-link}
import vizro.models as vm
from vizro import Vizro

page = vm.Page(
title="Button with text",
components=[vm.Button(text="I'm a button!")],
)

dashboard = vm.Dashboard(pages=[page])
Vizro().build(dashboard).run()
```
=== "app.yaml"
```yaml
# Still requires a .py to add data to the data manager and parse YAML configuration
# See from_yaml example
pages:
- components:
- type: button
text: I'm a button!
title: Button with text
```
=== "Result"
[![ButtonText]][ButtonText]

[ButtonText]: ../../assets/user_guides/components/button_text.png


### Create a link button

To navigate to a different page using a button with an anchor tag, assign an absolute or relative URL to the
`Button.href`.

```python
import vizro.models as vm

vm.Button(text="Leave us a star! ⭐", href="https://github.com/mckinsey/vizro")
```

### Attach an action

You can use the [`Button`][vizro.models.Button] to trigger predefined action functions, such as exporting data.
To explore the available options for [`Actions`][vizro.models.Action], refer to our [API reference][vizro.actions].
Use the `Button.actions` argument to specify which action function executes when the button is clicked.

The example below demonstrates how to configure a button to export the filtered data of a target chart using the
[export_data][vizro.actions.export_data] action function.


!!! example "Button with action"

=== "app.py"
```{.python pycafe-link}
Expand Down Expand Up @@ -681,6 +733,7 @@ In the below example we show how to configure a button to export the filtered da

[Button]: ../../assets/user_guides/components/button.png

### Use as a control
The [`Button`][vizro.models.Button] component is currently reserved to be used inside the main panel (right-side) of the dashboard.
However, there might be use cases where one would like to place the `Button` inside the control panel (left-side) with the other controls.

Expand Down
52 changes: 4 additions & 48 deletions vizro-core/examples/scratch_dev/app.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,15 @@
"""Dev app to try things out."""

import pandas as pd
import plotly.graph_objects as go
import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro.models.types import capture


@capture("graph")
def lollipop(data_frame: pd.DataFrame, x: str, y: str):
"""Creates a lollipop chart using Plotly.

This function generates a scatter chart and then draws lines extending from each point to the x-axis.

Args:
data_frame (pd.DataFrame): The data source for the chart.
x (str): The column name to be used for the x-axis.
y (str): The column name to be used for the y-axis.

Returns:
go.Figure: : A Plotly Figure object representing the lollipop chart.
"""
fig = go.Figure()

# Draw points
fig.add_trace(
go.Scatter(
x=data_frame[x],
y=data_frame[y],
mode="markers",
marker=dict(color="#00b4ff", size=12),
)
)

for i in range(len(data_frame)):
fig.add_trace(
go.Scatter(
x=[0, data_frame[x].iloc[i]],
y=[data_frame[y].iloc[i], data_frame[y].iloc[i]],
mode="lines",
line=dict(color="#00b4ff", width=3),
)
)
fig.update_layout(showlegend=False)
return fig


gapminder = px.data.gapminder()


page = vm.Page(
title="Lollipop",
title="Button Styling",
layout=vm.Layout(grid=[[0, 1]]),
components=[
vm.Graph(figure=lollipop(gapminder.query("year == 2007 and gdpPercap > 36000"), y="country", x="gdpPercap"))
vm.Button(),
vm.Button(text="Take me home", href="/"),
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ img[src*="#chart-icon"] {
position: relative;
}

.code-clipboard-container .pycafe-link,
.code-clipboard-container .pycafe-link:focus {
line-height: unset;
.code-clipboard-container .pycafe-link {
margin-bottom: 12px;
}

Expand Down
6 changes: 6 additions & 0 deletions vizro-core/schemas/0.1.28.dev0.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"default": "Click me!",
"type": "string"
},
"href": {
"title": "Href",
"description": "URL (relative or absolute) to navigate to.",
"default": "",
"type": "string"
},
"actions": {
"title": "Actions",
"default": [],
Expand Down
9 changes: 8 additions & 1 deletion vizro-core/src/vizro/models/_components/button.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Literal

import dash_bootstrap_components as dbc
from dash import get_relative_path

try:
from pydantic.v1 import Field
Expand All @@ -24,11 +25,17 @@ class Button(VizroBaseModel):

type: Literal["button"] = "button"
text: str = Field("Click me!", description="Text to be displayed on button.")
href: str = Field("", description="URL (relative or absolute) to navigate to.")
actions: list[Action] = []

# Re-used validators
_set_actions = _action_validator_factory("n_clicks")

@_log_call
def build(self):
return dbc.Button(id=self.id, children=self.text)
return dbc.Button(
id=self.id,
children=self.text,
href=get_relative_path(self.href) if self.href.startswith("/") else self.href,
target="_top",
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
)
2 changes: 1 addition & 1 deletion vizro-core/src/vizro/static/css/vizro-bootstrap.min.css

Large diffs are not rendered by default.

31 changes: 25 additions & 6 deletions vizro-core/tests/unit/vizro/models/_components/test_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@ def test_create_default_button(self):
assert hasattr(button, "id")
assert button.type == "button"
assert button.text == "Click me!"
assert button.href == ""
assert button.actions == []

@pytest.mark.parametrize("text", ["Test", 123, 1.23, True, """# Header""", """<p>Hello </p>"""])
def test_create_button_with_optional(self, text):
button = vm.Button(text=text)
assert hasattr(button, "id")
@pytest.mark.parametrize(
"text, href",
[
("Test", "/page_1_reference"),
("Test", "https://www.google.de/"),
(123, "/"),
("""# Header""", "/"),
(1.23, "/"),
("""<p>Hello </p>""", "/"),
(True, "/"),
],
)
def test_create_button_with_optional(self, text, href):
button = vm.Button(id="button-id", text=text, href=href)

assert button.id == "button-id"
assert button.type == "button"
assert button.text == str(text)
assert button.href == href
assert button.actions == []

def test_set_action_via_validator(self):
Expand All @@ -33,7 +47,12 @@ def test_set_action_via_validator(self):


class TestBuildMethod:
def test_button_build(self):
def test_button_build_wo_href(self):
button = vm.Button(id="button_id", text="My text").build()
expected = dbc.Button(id="button_id", children="My text")
expected = dbc.Button(id="button_id", children="My text", href="", target="_top")
assert_component_equal(button, expected)

def test_button_build_with_href(self):
button = vm.Button(id="button_id", text="My text", href="https://www.google.com").build()
expected = dbc.Button(id="button_id", children="My text", href="https://www.google.com", target="_top")
assert_component_equal(button, expected)
2 changes: 1 addition & 1 deletion vizro-core/tests/unit/vizro/models/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_field_invalid_pages_empty_list(self):
vm.Dashboard(pages=[])

def test_field_invalid_pages_input_type(self):
with pytest.raises(ValidationError, match="4 validation errors for Dashboard"):
with pytest.raises(ValidationError, match="5 validation errors for Dashboard"):
vm.Dashboard(pages=[vm.Button()])

def test_field_invalid_theme_input_type(self, page_1):
Expand Down