From f7750d48fab68eb656bb6d95af0408de696f9483 Mon Sep 17 00:00:00 2001 From: Li Nguyen <90609403+huong-li-nguyen@users.noreply.github.com> Date: Tue, 9 Jul 2024 13:34:26 +0200 Subject: [PATCH] [Feat] Update `optionHeight` of `vm.Dropdown` dynamically (#574) --- ...9_huong_li_nguyen_fix_default_nav_items.md | 2 +- ..._li_nguyen_enable_dynamic_option_height.md | 47 ++++++++++++++++ vizro-core/examples/_dev/app.py | 56 +++++++++---------- .../vizro/models/_components/form/dropdown.py | 3 + .../models/_components/form/test_dropdown.py | 31 ++++++++++ 5 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 vizro-core/changelog.d/20240708_185435_huong_li_nguyen_enable_dynamic_option_height.md diff --git a/vizro-core/changelog.d/20240708_105029_huong_li_nguyen_fix_default_nav_items.md b/vizro-core/changelog.d/20240708_105029_huong_li_nguyen_fix_default_nav_items.md index 5beb71ca9..b97b8932d 100644 --- a/vizro-core/changelog.d/20240708_105029_huong_li_nguyen_fix_default_nav_items.md +++ b/vizro-core/changelog.d/20240708_105029_huong_li_nguyen_fix_default_nav_items.md @@ -37,7 +37,7 @@ Uncomment the section that is right (remove the HTML comment wrapper). ### Fixed -- Remove default icon provision for `vm.NavLink` when the icon count exceeds 9 and a user icon is provided.([#571](https://github.com/mckinsey/vizro/pull/571)) +- Remove default icon provision for `vm.NavLink` when the icon count exceeds 9 and a user icon is provided.([#572](https://github.com/mckinsey/vizro/pull/572)) + + + + + +### Changed + +- Update `optionHeight` of `vm.Dropdown` dynamically based on character length. ([#574](https://github.com/mckinsey/vizro/pull/574)) + + + + diff --git a/vizro-core/examples/_dev/app.py b/vizro-core/examples/_dev/app.py index 77a1e670d..973fa7292 100644 --- a/vizro-core/examples/_dev/app.py +++ b/vizro-core/examples/_dev/app.py @@ -1,38 +1,38 @@ """Dev app to try things out.""" +import numpy as np import vizro.models as vm +import vizro.plotly.express as px from vizro import Vizro -page_1 = vm.Page(title="Page 1", components=[vm.Card(text="Placeholder")]) -page_2 = vm.Page(title="Page 2", components=[vm.Card(text="Placeholder")]) -page_3 = vm.Page(title="Page 3", components=[vm.Card(text="Placeholder")]) -page_4 = vm.Page(title="Page 4", components=[vm.Card(text="Placeholder")]) -page_5 = vm.Page(title="Page 5", components=[vm.Card(text="Placeholder")]) -page_6 = vm.Page(title="Page 6", components=[vm.Card(text="Placeholder")]) -page_7 = vm.Page(title="Page 7", components=[vm.Card(text="Placeholder")]) -page_8 = vm.Page(title="Page 8", components=[vm.Card(text="Placeholder")]) -page_9 = vm.Page(title="Page 9", components=[vm.Card(text="Placeholder")]) -page_10 = vm.Page(title="Page 10", components=[vm.Card(text="Placeholder")]) +df = px.data.iris() +df["species_one_long"] = np.where( + df["species"] == "setosa", "setosa is one common species you can select in the iris dataset.", df["species"] +) +df["species_long"] = df["species"] + " is one common species you can select in the iris dataset." +df["species_very_long"] = ( + df["species"] + + " is one common species you can select in the iris dataset is one common species you can select in the iris data." +) -dashboard = vm.Dashboard( - pages=[page_1, page_2, page_3, page_4, page_5, page_6, page_7, page_8, page_9, page_10], - navigation=vm.Navigation( - nav_selector=vm.NavBar( - items=[ - vm.NavLink(label="Page 1", pages=["Page 1"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 2"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 3"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 4"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 5"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 6"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 7"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 8"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 9"], icon="Home"), - vm.NavLink(label="Page 1", pages=["Page 10"], icon="Home"), - ] - ) - ), +page = vm.Page( + title="", + components=[ + vm.Graph( + id="graph_1", + figure=px.scatter(df, title="Title", x="sepal_width", y="sepal_length", color="species"), + ), + ], + controls=[ + vm.Filter(column="species"), + vm.Filter(column="species_long"), + vm.Filter(column="species_one_long"), + vm.Filter(column="species_very_long"), + ], ) + +dashboard = vm.Dashboard(pages=[page]) + if __name__ == "__main__": Vizro().build(dashboard).run() diff --git a/vizro-core/src/vizro/models/_components/form/dropdown.py b/vizro-core/src/vizro/models/_components/form/dropdown.py index 0b9ea4c6b..1eb84679a 100755 --- a/vizro-core/src/vizro/models/_components/form/dropdown.py +++ b/vizro-core/src/vizro/models/_components/form/dropdown.py @@ -1,3 +1,4 @@ +import math from typing import List, Literal, Optional, Union from dash import dcc, html @@ -71,6 +72,8 @@ def build(self): value=self.value if self.value is not None else default_value, multi=self.multi, persistence=True, + # 37 is the cut-off character length where the text inside the dropdown starts to wrap + optionHeight=32 + 24 * math.floor(max(len(str(option)) for option in full_options) / 37), persistence_type="session", ), ] diff --git a/vizro-core/tests/unit/vizro/models/_components/form/test_dropdown.py b/vizro-core/tests/unit/vizro/models/_components/form/test_dropdown.py index 66145424a..1ab6ba483 100755 --- a/vizro-core/tests/unit/vizro/models/_components/form/test_dropdown.py +++ b/vizro-core/tests/unit/vizro/models/_components/form/test_dropdown.py @@ -155,6 +155,7 @@ def test_dropdown_with_all_option(self): dcc.Dropdown( id="dropdown_id", options=["ALL", "A", "B", "C"], + optionHeight=32, value="ALL", multi=True, persistence=True, @@ -173,6 +174,7 @@ def test_dropdown_without_all_option(self): dcc.Dropdown( id="dropdown_id", options=["A", "B", "C"], + optionHeight=32, value="A", multi=False, persistence=True, @@ -182,3 +184,32 @@ def test_dropdown_without_all_option(self): ) assert_component_equal(dropdown, expected_dropdown) + + @pytest.mark.parametrize( + "options, option_height", + [ + (["A", "B", "C"], 32), + ([10, 20, 30], 32), + (["A text with a length of 36..........", "B", "C"], 32), + (["A text with a length of 37...........", "B", "C"], 56), + (["A text with a length of 37..........." + "A text with a length of 37...........", "B", "C"], 80), + ], + ) + def test_dropdown_dynamic_option_height(self, options, option_height): + dropdown = Dropdown(id="dropdown_id", multi=False, options=options).build() + expected_dropdown = html.Div( + [ + None, + dcc.Dropdown( + id="dropdown_id", + options=options, + optionHeight=option_height, + multi=False, + value=options[0], + persistence=True, + persistence_type="session", + ), + ] + ) + + assert_component_equal(dropdown, expected_dropdown)