diff --git a/vizro-core/changelog.d/20241204_135618_huong_li_nguyen_remove_custom_img_css.md b/vizro-core/changelog.d/20241204_135618_huong_li_nguyen_remove_custom_img_css.md new file mode 100644 index 000000000..750a8d283 --- /dev/null +++ b/vizro-core/changelog.d/20241204_135618_huong_li_nguyen_remove_custom_img_css.md @@ -0,0 +1,48 @@ + + + + +### Removed + +- Remove built-in CSS shortcuts `#floating-*` to float images. These can still be provided manually. ([#919](https://github.com/mckinsey/vizro/pull/919)) + + + + + + + diff --git a/vizro-core/docs/assets/tutorials/dashboard/dashboard-first-page.png b/vizro-core/docs/assets/tutorials/dashboard/dashboard-first-page.png new file mode 100644 index 000000000..ce421fca7 Binary files /dev/null and b/vizro-core/docs/assets/tutorials/dashboard/dashboard-first-page.png differ diff --git a/vizro-core/docs/assets/tutorials/dashboard/dashboard-second-page.png b/vizro-core/docs/assets/tutorials/dashboard/dashboard-second-page.png new file mode 100644 index 000000000..ee48af4d9 Binary files /dev/null and b/vizro-core/docs/assets/tutorials/dashboard/dashboard-second-page.png differ diff --git a/vizro-core/docs/pages/tutorials/explore-components.md b/vizro-core/docs/pages/tutorials/explore-components.md index a770dacf1..f59cbf052 100644 --- a/vizro-core/docs/pages/tutorials/explore-components.md +++ b/vizro-core/docs/pages/tutorials/explore-components.md @@ -104,7 +104,7 @@ The code below adds two components to the page: id="line_gdp", figure=px.line(gapminder_data, x="year", y="gdpPercap", color="continent", labels={"year": "Year", "continent": "Continent", - "gdpPercap":"GDP Per Cap"}, title=''), + "gdpPercap":"GDP Per Cap"}), ) ``` @@ -137,7 +137,7 @@ The code below adds two components to the page: vm.Graph( figure=px.line(gapminder_data, x="year", y="gdpPercap", color="continent", labels={"year": "Year", "continent": "Continent", - "gdpPercap":"GDP Per Cap"}, title=''), + "gdpPercap":"GDP Per Cap"}), ), ], @@ -460,46 +460,15 @@ You can apply selectors to configure [`Filters`][vizro.models.Filter] and [`Para ## 4. The final touches -This section puts everything together by adding a homepage to the example for navigation between the two separate pages. - -For easy navigation within your dashboard, we'll create a page that serves as the entry point for the user. On this homepage are two [`Cards`][vizro.models.Card] which serve as tiles that can be customized with a title, some text, and an image. These cards link to the subpages within your dashboard using their `href` attributes as `href="/first-page"` and `href="/second-page"`. This establishes the navigation links from the homepage to each of the subpages. - -Each page is added to the dashboard using the following line of code: `vm.Dashboard(pages=[home_page, first_page, second_page])`. This ensures that all the pages are accessible. +Each page is added to the dashboard using the following line of code: `vm.Dashboard(pages=[first_page, second_page])`. This ensures that all the pages are accessible. -The code below illustrates a functional dashboard where you can navigate from the homepage to each of the subpages. Additionally, you can use the navigation panel on the left side to switch between the three pages. +By default, a navigation panel on the left side enables the user to switch between the two pages. !!! example "Final dashboard" === "Code" ```python - home_page = vm.Page( - title="Homepage", - components=[ - vm.Card( - text=""" - ![](https://raw.githubusercontent.com/mckinsey/vizro/786167c822cce65fe85ffad8ed000d8553a5ef44/vizro-core/docs/assets/images/collections.svg#icon-top) - - ### First Page - - Exemplary first dashboard page. - """, - href="/first-page", - ), - vm.Card( - text=""" - ![](https://raw.githubusercontent.com/mckinsey/vizro/786167c822cce65fe85ffad8ed000d8553a5ef44/vizro-core/docs/assets/images/features.svg#icon-top) - - ### Second Page - - Exemplary second dashboard page. - """, - href="/second-page", - ), - ], - ) - - ... - dashboard = vm.Dashboard(pages=[home_page, first_page, second_page]) + Vizro().build(dashboard).run() ``` === "app.py" @@ -509,32 +478,6 @@ The code below illustrates a functional dashboard where you can navigate from th import vizro.models as vm import vizro.plotly.express as px - home_page = vm.Page( - title="Homepage", - components=[ - vm.Card( - text=""" - ![](https://raw.githubusercontent.com/mckinsey/vizro/786167c822cce65fe85ffad8ed000d8553a5ef44/vizro-core/docs/assets/images/collections.svg) - - ### First Page - - Exemplary first dashboard page. - """, - href="/first-page", - ), - vm.Card( - text=""" - ![](https://raw.githubusercontent.com/mckinsey/vizro/786167c822cce65fe85ffad8ed000d8553a5ef44/vizro-core/docs/assets/images/features.svg#icon-top) - - ### Second Page - - Exemplary second dashboard page. - """, - href="/second-page", - ), - ], - ) - df = px.data.gapminder() gapminder_data = ( df.groupby(by=["continent", "year"]). @@ -603,13 +546,10 @@ The code below illustrates a functional dashboard where you can navigate from th ], ) - dashboard = vm.Dashboard(pages=[home_page, first_page, second_page]) + dashboard = vm.Dashboard(pages=[first_page, second_page]) Vizro().build(dashboard).run() ``` - === "Homepage" - [![FinalPage]][finalpage] - === "Subpage1" [![FinalPage1]][finalpage1] @@ -631,9 +571,8 @@ Vizro doesn't end here, and we only covered the key features, but there is still - How to use [Actions](../user-guides/actions.md) for example, for chart interaction or custom controls. - How to create dashboards from `yaml`, `dict` or `json` following the [dashboard guide](../user-guides/dashboard.md). -[finalpage]: ../../assets/tutorials/dashboard/dashboard4.png -[finalpage1]: ../../assets/tutorials/dashboard/dashboard2.png -[finalpage2]: ../../assets/tutorials/dashboard/dashboard3.png +[finalpage1]: ../../assets/tutorials/dashboard/dashboard-first-page.png +[finalpage2]: ../../assets/tutorials/dashboard/dashboard-second-page.png [firstpage1]: ../../assets/tutorials/dashboard/dashboard21.png [firstpage2]: ../../assets/tutorials/dashboard/dashboard22.png [firstpage3]: ../../assets/tutorials/dashboard/dashboard23.png diff --git a/vizro-core/docs/pages/user-guides/card-button.md b/vizro-core/docs/pages/user-guides/card-button.md index 3c5ad2d7d..7da72d8f1 100755 --- a/vizro-core/docs/pages/user-guides/card-button.md +++ b/vizro-core/docs/pages/user-guides/card-button.md @@ -322,11 +322,12 @@ Note the added URL hash `#my-image`. Now create a CSS file placed in your `asset Use the following pre-defined URL hashes in your image path to apply Vizro's default styling. -#### To float the image next to the text: +**To float the image next to the text:** -- floating-left: `![](my_image.png#floating-left)` -- floating-right: `![](my_image.png#floating-right)` -- floating-center: `![](my_image.png#floating-center)` +To float an image for example to the right of the text, use the `src` attribute as a CSS selector. Follow these steps: + +1. Add a hash (#) to the image URL, e.g., `![](assets/images/continents/europe.svg#my-image)` +1. Target that hash in your custom CSS file `img[src*="#my-image"] { float: right; }` !!! example "Card with floating image" === "images.css" @@ -334,6 +335,7 @@ Use the following pre-defined URL hashes in your image path to apply Vizro's def img[src*="#my-image"] { width: 120px; height: 120px; + float: right; } ``` @@ -349,7 +351,7 @@ Use the following pre-defined URL hashes in your image path to apply Vizro's def text=""" ### My card with floating image! - ![](assets/images/continents/europe.svg#my-image#floating-right) + ![](assets/images/continents/europe.svg#my-image) Commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit. @@ -381,7 +383,7 @@ Use the following pre-defined URL hashes in your image path to apply Vizro's def pages: - components: - text: | - ![](assets/images/continents/europe.svg#my-image#floating-right) + ![](assets/images/continents/europe.svg#my-image) Commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit. @@ -402,63 +404,17 @@ Use the following pre-defined URL hashes in your image path to apply Vizro's def === "Result" [![CardImageFloating]][cardimagefloating] -#### Card with icon - -- default icon styling (`icon-top`): `![](my_image.png#icon-top)` - -!!! example "Card with icon" - === "app.py" - ```{.python pycafe-link} - import vizro.models as vm - from vizro import Vizro - - page = vm.Page( - title="Card with icon", - components=[ - vm.Card( - text=""" - ![](https://raw.githubusercontent.com/mckinsey/vizro/d24a6f0d4efdf3c47392458e64b190fa1f92b2a7/vizro-core/docs/assets/images/hypotheses.svg#icon-top) - - ### Card Title - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut fringilla dictum lacus eget fringilla. - Maecenas in various nibh, quis venenatis nulla. Integer et libero ultrices, scelerisque velit sed. - """, - ), - ], - ) - - 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: - - text: | - ![](assets/images/icons/hypotheses.svg#icon-top) - - ### Card Title - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut fringilla dictum lacus eget fringilla. - Maecenas in various nibh, quis venenatis nulla. Integer et libero ultrices, scelerisque velit sed. - type: card - title: Card with icon - ``` +### Make an icon responsive to theme switch - === "Result" - [![CardIcon]][cardicon] +To make an icon responsive to theme switching, override its [`filter` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/filter). -### Make an icon responsive to theme switch +In this example, we use the `--fill-icon-image-card` CSS variable from the `vizro-bootstrap` CSS file. It uses the `invert()` function to flip the icon's color during a theme switch. -To make an icon responsive to the theme switch, override the value of the [`filter` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/filter). The `filter` CSS property lets you add visual effects to elements using different functions. In our example, we're using the `--inverse-color` CSS variable from the Vizro theme. It uses the CSS `invert()` function to flip the color of the icon when you switch themes. Note that this only works if your initial icon has a white fill color. If your icon is not white, you can change its color by adding `fill="white"` to the SVG code. Assign the predefined CSS variable `--inverse-color` to the `filter` property of your selected icon. +This approach works if your icon initially has a white fill color. If not, modify the SVG code by adding `fill="white"`. ```css img[src*="#my-image"] { - filter: var(--inverse-color); + filter: var(--fill-icon-image-card); } ``` @@ -720,7 +676,6 @@ vm.Page.add_type("controls", vm.Button) [button]: ../../assets/user_guides/components/button.png [buttontext]: ../../assets/user_guides/components/button_text.png [card]: ../../assets/user_guides/components/card.png -[cardicon]: ../../assets/user_guides/components/card_icon.png [cardimagedefault]: ../../assets/user_guides/components/card_image_default.png [cardimagefloating]: ../../assets/user_guides/components/card_image_floating.png [cardimagestyled]: ../../assets/user_guides/components/card_image_styled.png diff --git a/vizro-core/examples/dev/assets/css/custom.css b/vizro-core/examples/dev/assets/css/custom.css index 57c91b902..749289dba 100644 --- a/vizro-core/examples/dev/assets/css/custom.css +++ b/vizro-core/examples/dev/assets/css/custom.css @@ -42,3 +42,9 @@ img#banner { height: 16px; } + +img[src*="icon-top"] { + filter: var(--fill-icon-image-card); + height: 36px; + width: 36px; +} diff --git a/vizro-core/examples/scratch_dev/app.py b/vizro-core/examples/scratch_dev/app.py index 66b23823d..56413e831 100644 --- a/vizro-core/examples/scratch_dev/app.py +++ b/vizro-core/examples/scratch_dev/app.py @@ -1,248 +1,89 @@ """Dev app to try things out.""" -import time -import yaml - -import dash -import pandas as pd -from flask_caching import Cache - +from vizro import Vizro import vizro.models as vm import vizro.plotly.express as px -from vizro import Vizro -from vizro.managers import data_manager -from functools import partial - -print("INITIALIZING") - -SPECIES_COLORS = {"setosa": "#00b4ff", "versicolor": "#ff9222", "virginica": "#3949ab"} -BAR_CHART_CONF = dict(x="species", color="species", color_discrete_map=SPECIES_COLORS) -SCATTER_CHART_CONF = dict(x="sepal_length", y="petal_length", color="species", color_discrete_map=SPECIES_COLORS) - - -def load_from_file(filter_column=None, parametrized_species=None): - # Load the full iris dataset - df = px.data.iris() - df["date_column"] = pd.date_range(start=pd.to_datetime("2024-01-01"), periods=len(df), freq="D") - - with open("data.yaml", "r") as file: - data = { - "setosa": 0, - "versicolor": 0, - "virginica": 0, - "min": 0, - "max": 10, - "date_min": "2024-01-01", - "date_max": "2024-05-29", - } - data.update(yaml.safe_load(file) or {}) - if filter_column == "species": - df = pd.concat( - objs=[ - df[df[filter_column] == "setosa"].head(data["setosa"]), - df[df[filter_column] == "versicolor"].head(data["versicolor"]), - df[df[filter_column] == "virginica"].head(data["virginica"]), - ], - ignore_index=True, - ) - elif filter_column == "sepal_length": - df = df[df[filter_column].between(data["min"], data["max"], inclusive="both")] - elif filter_column == "date_column": - date_min = pd.to_datetime(data["date_min"]) - date_max = pd.to_datetime(data["date_max"]) - df = df[df[filter_column].between(date_min, date_max, inclusive="both")] - else: - raise ValueError("Invalid filter_column") - - if parametrized_species: - df = df[df["species"].isin(parametrized_species)] - - return df - - -data_manager["load_from_file_species"] = partial(load_from_file, filter_column="species") -data_manager["load_from_file_sepal_length"] = partial(load_from_file, filter_column="sepal_length") -data_manager["load_from_file_date_column"] = partial(load_from_file, filter_column="date_column") - - -# TODO-DEV: Turn on/off caching to see how it affects the app. -# data_manager.cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 10}) - - -homepage = vm.Page( - title="Homepage", - components=[ - vm.Card(text="This is the homepage."), - ], +df = px.data.gapminder() +gapminder_data = ( + df.groupby(by=["continent", "year"]).agg({"lifeExp": "mean", "pop": "sum", "gdpPercap": "mean"}).reset_index() ) - -page_1 = vm.Page( - title="Dynamic vs Static filter", +first_page = vm.Page( + title="First Page", + layout=vm.Layout(grid=[[0, 0], [1, 2], [1, 2], [1, 2]]), components=[ - vm.Graph( - id="p1-G-1", - figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF), + vm.Card( + text=""" + # First dashboard page + This pages shows the inclusion of markdown text in a page and how components + can be structured using Layout. + """, ), vm.Graph( - id="p1-G-2", - figure=px.scatter(data_frame=px.data.iris(), **SCATTER_CHART_CONF), - ), - ], - controls=[ - vm.Filter(id="p1-F-1", column="species", targets=["p1-G-1"], selector=vm.Dropdown(title="Dynamic filter")), - vm.Filter(id="p1-F-2", column="species", targets=["p1-G-2"], selector=vm.Dropdown(title="Static filter")), - vm.Parameter( - targets=["p1-G-1.x", "p1-G-2.x"], - selector=vm.RadioItems(options=["species", "sepal_width"], title="Simple X-axis parameter"), - ), - ], -) - - -page_2 = vm.Page( - title="Categorical dynamic selectors", - components=[ - vm.Graph( - id="p2-G-1", - figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF), - ), - ], - controls=[ - vm.Filter(id="p2-F-1", column="species", selector=vm.Dropdown()), - vm.Filter(id="p2-F-2", column="species", selector=vm.Dropdown(multi=False)), - vm.Filter(id="p2-F-3", column="species", selector=vm.Checklist()), - vm.Filter(id="p2-F-4", column="species", selector=vm.RadioItems()), - vm.Parameter( - targets=["p2-G-1.x"], - selector=vm.RadioItems( - options=["species", "sepal_width"], value="species", title="Simple X-axis parameter" + id="box_cont", + figure=px.box( + gapminder_data, + x="continent", + y="lifeExp", + color="continent", + labels={"lifeExp": "Life Expectancy", "continent": "Continent"}, ), ), - ], -) - - -page_3 = vm.Page( - title="Numerical dynamic selectors", - components=[ vm.Graph( - id="p3-G-1", - figure=px.bar(data_frame="load_from_file_sepal_length", **BAR_CHART_CONF), - ), - ], - controls=[ - vm.Filter(id="p3-F-1", column="sepal_length", selector=vm.Slider()), - vm.Filter(id="p3-F-2", column="sepal_length", selector=vm.RangeSlider()), - vm.Parameter( - targets=["p3-G-1.x"], - selector=vm.RadioItems( - options=["species", "sepal_width"], value="species", title="Simple X-axis parameter" + id="line_gdp", + figure=px.line( + gapminder_data, + x="year", + y="gdpPercap", + color="continent", + labels={"year": "Year", "continent": "Continent", "gdpPercap": "GDP Per Cap"}, ), ), ], -) - -page_4 = vm.Page( - title="[TO BE DONE IN THE FOLLOW UP PR] Temporal dynamic selectors", - components=[ - vm.Graph( - id="p4-G-1", - figure=px.bar(data_frame="load_from_file_date_column", **BAR_CHART_CONF), - ), - ], controls=[ - vm.Filter(id="p4-F-1", column="date_column", selector=vm.DatePicker(range=False)), - vm.Filter(id="p4-F-2", column="date_column", selector=vm.DatePicker()), - vm.Parameter( - targets=["p4-G-1.x"], - selector=vm.RadioItems( - options=["species", "sepal_width"], value="species", title="Simple X-axis parameter" - ), - ), + vm.Filter(column="continent", targets=["box_cont", "line_gdp"]), ], ) -page_5 = vm.Page( - title="Parametrised dynamic selectors", +iris_data = px.data.iris() +second_page = vm.Page( + title="Second Page", components=[ vm.Graph( - id="p5-G-1", - figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF), - ), - ], - controls=[ - vm.Filter(id="p5-F-1", column="species", targets=["p5-G-1"], selector=vm.Checklist()), - vm.Parameter( - targets=[ - "p5-G-1.data_frame.parametrized_species", - # TODO: Uncomment the following target and see the magic :D - # Is this the indicator that parameter.targets prop has to support 'target' definition without the '.'? - # "p5-F-1.", - ], - selector=vm.Dropdown( - options=["setosa", "versicolor", "virginica"], multi=True, title="Parametrized species" + id="scatter_iris", + figure=px.scatter( + iris_data, + x="sepal_width", + y="sepal_length", + color="species", + color_discrete_map={"setosa": "#00b4ff", "versicolor": "#ff9222"}, + labels={"sepal_width": "Sepal Width", "sepal_length": "Sepal Length", "species": "Species"}, ), ), - vm.Parameter( - targets=[ - "p5-G-1.x", - # TODO: Uncomment the following target and see the magic :D - # "p5-F-1.", - ], - selector=vm.RadioItems( - options=["species", "sepal_width"], value="species", title="Simple X-axis parameter" - ), - ), - ], -) - - -page_6 = vm.Page( - title="Page to test things out", - components=[ - vm.Graph(id="graph_dynamic", figure=px.bar(data_frame="load_from_file_species", **BAR_CHART_CONF)), vm.Graph( - id="graph_static", - figure=px.scatter(data_frame=px.data.iris(), **SCATTER_CHART_CONF), + id="hist_iris", + figure=px.histogram( + iris_data, + x="sepal_width", + color="species", + color_discrete_map={"setosa": "#00b4ff", "versicolor": "#ff9222"}, + labels={"sepal_width": "Sepal Width", "count": "Count", "species": "Species"}, + ), ), ], controls=[ - vm.Filter( - id="filter_container_id", - column="species", - targets=["graph_dynamic"], - # targets=["graph_static"], - # selector=vm.Dropdown(id="filter_id"), - # selector=vm.Dropdown(id="filter_id", value=["setosa"]), - # selector=vm.Checklist(id="filter_id"), - # selector=vm.Checklist(id="filter_id", value=["setosa"]), - # TODO-BUG: vm.Dropdown(multi=False) Doesn't work if value is cleared. The persistence storage become - # "null" and our placeholder component dmc.DateRangePicker can't process null value. It expects a value or - # a list of values. - # SOLUTION -> Create the "Universal Vizro placeholder component". - # TEMPORARY SOLUTION -> set clearable=False for the dynamic Dropdown(multi=False) - # selector=vm.Dropdown(id="filter_id", multi=False), - # selector=vm.Dropdown(id="filter_id", multi=False, value="setosa"), - # selector=vm.RadioItems(id="filter_id"), - # selector=vm.RadioItems(id="filter_id", value="setosa"), - # selector=vm.Slider(id="filter_id"), - # selector=vm.Slider(id="filter_id", value=5), - # selector=vm.RangeSlider(id="filter_id"), - # selector=vm.RangeSlider(id="filter_id", value=[5, 7]), + vm.Parameter( + targets=["scatter_iris.color_discrete_map.virginica", "hist_iris.color_discrete_map.virginica"], + selector=vm.Dropdown(options=["#ff5267", "#3949ab"], multi=False, value="#3949ab", title="Color Virginica"), ), vm.Parameter( - targets=["graph_dynamic.x"], - selector=vm.RadioItems(options=["species", "sepal_width"], title="Simple X-axis parameter"), + targets=["scatter_iris.opacity"], + selector=vm.Slider(min=0, max=1, value=0.8, title="Opacity"), ), ], ) -dashboard = vm.Dashboard(pages=[homepage, page_1, page_2, page_3, page_4, page_5, page_6]) +dashboard = vm.Dashboard(pages=[first_page, second_page]) if __name__ == "__main__": - app = Vizro().build(dashboard) - - print("RUNNING\n") - - app.run(dev_tools_hot_reload=False) + Vizro().build(dashboard).run() diff --git a/vizro-core/src/vizro/static/css/images.css b/vizro-core/src/vizro/static/css/images.css deleted file mode 100644 index df3e4a4d0..000000000 --- a/vizro-core/src/vizro/static/css/images.css +++ /dev/null @@ -1,21 +0,0 @@ -img[src*="floating-right"] { - float: right; -} - -img[src*="floating-left"] { - float: left; -} - -img[src*="floating-center"] { - display: block; - float: none; - height: auto; - margin: auto; - max-width: 100%; -} - -img[src*="icon-top"] { - filter: var(--fill-icon-image-card); - height: 36px; - width: 36px; -} diff --git a/vizro-core/tests/unit/vizro/models/_components/test_card.py b/vizro-core/tests/unit/vizro/models/_components/test_card.py index 44c9d24ab..9b4274653 100755 --- a/vizro-core/tests/unit/vizro/models/_components/test_card.py +++ b/vizro-core/tests/unit/vizro/models/_components/test_card.py @@ -77,8 +77,8 @@ def test_card_build_wo_href(self): ("Text to test card", "Text to test card"), ("", ""), ( - """![](assets/images/icons/content/hypotheses.svg#icon-top)""", - "![](assets/images/icons/content/hypotheses.svg#icon-top)", + """![](assets/images/icons/content/hypotheses.svg)""", + "![](assets/images/icons/content/hypotheses.svg)", ), ("""Code block: ```python print(1)```""", "Code block: ```python print(1)```"), ("""[Example page](/test_page)""", "[Example page](/test_page)"),