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

Rearrange and rename main page containers #205

Merged
merged 46 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f46c32b
Arrange containers
huong-li-nguyen Dec 11, 2023
359a1c8
Update CSS
huong-li-nguyen Dec 11, 2023
ddd337a
Fix bug on one of the layouts
huong-li-nguyen Dec 11, 2023
8286a36
Add logo and banner for testing
huong-li-nguyen Dec 11, 2023
136ecd0
Refactor `_make_page_layout` into sub-functions
huong-li-nguyen Dec 11, 2023
47096bf
Add notes and rename
huong-li-nguyen Dec 12, 2023
93a1fcb
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 18, 2023
752aa75
Remove logo test code
huong-li-nguyen Dec 18, 2023
d96147b
Consolidate arrangement function and replace TypedDict with html.Div
huong-li-nguyen Dec 18, 2023
59e6f21
Refactor if/else check into helper function
huong-li-nguyen Dec 18, 2023
fefb36a
Fix if/else check for None type
huong-li-nguyen Dec 18, 2023
3c789e7
Add changelog
huong-li-nguyen Dec 18, 2023
88d29ab
Refactor helper function for hideable parent container and add tests
huong-li-nguyen Dec 18, 2023
8c9a831
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 19, 2023
18af365
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 19, 2023
f3f3faa
Remove className from NavBar and use ID only
huong-li-nguyen Dec 19, 2023
809fdf6
Remove className from NavPanel and use ID only
huong-li-nguyen Dec 19, 2023
0d9be98
Remove className from ControlPanel and use ID only
huong-li-nguyen Dec 19, 2023
7510d94
Fix CSS
huong-li-nguyen Dec 19, 2023
3d39a6d
Remove className for page_container and use ID only
huong-li-nguyen Dec 19, 2023
ee4c6bd
Remove className for components and use ID only
huong-li-nguyen Dec 19, 2023
412fd86
Minor fixes
huong-li-nguyen Dec 19, 2023
68cdf72
Remove children keyword argument
huong-li-nguyen Dec 19, 2023
0d900bc
Revert change
huong-li-nguyen Dec 19, 2023
467e8d9
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 19, 2023
67fc091
Revert CSS canges
huong-li-nguyen Dec 19, 2023
f4b902c
Apply hyphens to CSS IDs and change TypedDict
huong-li-nguyen Dec 19, 2023
9210ca3
Test out _is_hidden
huong-li-nguyen Dec 19, 2023
49575ac
Add important CSS fix
huong-li-nguyen Dec 19, 2023
d9b7ea8
Fix typo in test
huong-li-nguyen Dec 19, 2023
f5f883d
Fix CSS
huong-li-nguyen Dec 20, 2023
b98e046
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 20, 2023
356dc2d
Lint and fix test
huong-li-nguyen Dec 20, 2023
c35e1b7
Edit changelog
huong-li-nguyen Dec 20, 2023
dd51481
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 20, 2023
f2fb529
Remove bg color from inner containers and add to outer
huong-li-nguyen Dec 21, 2023
8a156f9
Add fix for span not being colored
huong-li-nguyen Dec 21, 2023
7bb3614
Remove dashboard container and fix typing
huong-li-nguyen Dec 21, 2023
6fac9d7
Clean up CSS classes
huong-li-nguyen Dec 21, 2023
5bc310d
Rename _all_hidden function
huong-li-nguyen Dec 21, 2023
0b8b399
PR comments
huong-li-nguyen Dec 21, 2023
9173a7a
Add back important tags
huong-li-nguyen Dec 21, 2023
67e8715
Update test coverage due to removal of non-working test
huong-li-nguyen Dec 21, 2023
bec7416
Replace dbc.Container with html.Div
huong-li-nguyen Dec 21, 2023
5adc769
Fix CSS
huong-li-nguyen Dec 21, 2023
1511e10
Merge branch 'main' into dev/arrange_containers
huong-li-nguyen Dec 21, 2023
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

- 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

- Re-arrange main containers on page and change their CSS IDs. ([#205](https://github.com/mckinsey/vizro/pull/205))

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

-->
83 changes: 60 additions & 23 deletions vizro-core/src/vizro/models/_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
from functools import partial
from typing import TYPE_CHECKING, List, Literal
from typing import TYPE_CHECKING, List, Literal, TypedDict

import dash
import dash_bootstrap_components as dbc
Expand All @@ -28,6 +28,29 @@
logger = logging.getLogger(__name__)


def _is_hidden(components: List[html.Div]):
"""Returns True if all components are either None and/or have hidden=True."""
return all(div is None or getattr(div, "hidden", False) for div in components)
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved


# This is just used for type checking. Ideally it would inherit from some dash.development.base_component.Component
# (e.g. html.Div) as well as TypedDict, but that's not possible, and Dash does not have typing support anyway. When
# this type is used, the object is actually still a dash.development.base_component.Component, but this makes it easier
# to see what contract the component fulfills by making the expected keys explicit.
_PageDivsType = TypedDict(
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
"_PageDivsType",
{
"dashboard-title": html.Div,
"settings": html.Div,
"page-title": html.H2,
"nav-bar": html.Div,
"nav-panel": html.Div,
"control-panel": html.Div,
"components": html.Div,
},
)


class Dashboard(VizroBaseModel):
"""Vizro Dashboard to be used within [`Vizro`][vizro._vizro.Vizro.build].

Expand Down Expand Up @@ -99,41 +122,55 @@ def build(self):
fluid=True,
)

def _make_page_layout(self, page: Page):
def _get_page_divs(self, page: Page) -> _PageDivsType:
# Identical across pages
dashboard_title = (
html.Div(children=[html.H2(self.title), html.Hr()], className="dashboard_title", id="dashboard_title_outer")
html.Div([html.H2(self.title)], id="dashboard-title")
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
if self.title
else html.Div(hidden=True, id="dashboard_title_outer")
else html.Div(id="dashboard-title", hidden=True)
)
theme_switch = daq.BooleanSwitch(
id="theme_selector", on=self.theme == "vizro_dark", persistence=True, persistence_type="session"
settings = html.Div(
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
daq.BooleanSwitch(
id="theme_selector", on=self.theme == "vizro_dark", persistence=True, persistence_type="session"
),
id="settings",
)

# Shared across pages but slightly differ in content. These could possibly be done by a clientside
# callback instead.
page_title = html.H2(children=page.title, id="page_title")
page_title = html.H2(page.title, id="page-title")
navigation: _NavBuildType = self.navigation.build(active_page_id=page.id)
nav_bar = navigation["nav_bar_outer"]
nav_panel = navigation["nav_panel_outer"]
nav_bar = navigation["nav-bar"]
nav_panel = navigation["nav-panel"]

# Different across pages
page_content: _PageBuildType = page.build()
control_panel = page_content["control_panel_outer"]
component_container = page_content["component_container_outer"]

# Arrangement
header = html.Div(children=[page_title, theme_switch], className="header", id="header_outer")
nav_control_elements = [dashboard_title, nav_panel, control_panel]
nav_control_panel = (
html.Div(nav_control_elements, className="nav_control_panel")
if any(not getattr(element, "hidden", False) for element in nav_control_elements)
else None
)
control_panel = page_content["control-panel"]
components = page_content["components"]
return html.Div([dashboard_title, settings, page_title, nav_bar, nav_panel, control_panel, components])

left_side = html.Div(children=[nav_bar, nav_control_panel], className="left_side", id="left_side_outer")
right_side = html.Div(children=[header, component_container], className="right_side", id="right_side_outer")
return html.Div([left_side, right_side], className="page_container", id="page_container_outer")
def _arrange_page_divs(self, page_divs: html.Div):
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
left_header_divs = [page_divs["dashboard-title"]]
left_sidebar_divs = [page_divs["nav-bar"]]
left_main_divs = [
html.Div(left_header_divs, id="left-header", hidden=_is_hidden(left_header_divs)),
page_divs["nav-panel"],
page_divs["control-panel"],
]

left_sidebar = html.Div(left_sidebar_divs, id="left-sidebar", hidden=_is_hidden(left_sidebar_divs))
left_main = html.Div(left_main_divs, id="left-main", hidden=_is_hidden(left_main_divs))
left_side = html.Div([left_sidebar, left_main], id="left-side")

right_header = html.Div([page_divs["page-title"], page_divs["settings"]], id="right-header")
right_main = page_divs["components"]
right_side = html.Div([right_header, right_main], id="right-side")

return html.Div([left_side, right_side], id="page-container")

def _make_page_layout(self, page: Page):
page_divs = self._get_page_divs(page=page)
return self._arrange_page_divs(page_divs=page_divs)

@staticmethod
def _make_page_404_layout():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,4 @@ def _validate_pages(pages):
# (e.g. html.Div) as well as TypedDict, but that's not possible, and Dash does not have typing support anyway. When
# this type is used, the object is actually still a dash.development.base_component.Component, but this makes it easier
# to see what contract the component fulfills by making the expected keys explicit.
class _NavBuildType(TypedDict):
nav_bar_outer: html.Div
nav_panel_outer: html.Div
_NavBuildType = TypedDict("_NavBuildType", {"nav-bar": html.Div, "nav-panel": html.Div})
7 changes: 3 additions & 4 deletions vizro-core/src/vizro/models/_navigation/accordion.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def coerce_pages_type(cls, pages):

@_log_call
def build(self, *, active_page_id=None):
# Note build does not return _NavBuildType but just a single html.Div with id="nav_panel_outer".
# Note build does not return _NavBuildType but just a single html.Div with id="nav-panel".
# Hide navigation panel if there is only one page
if len(list(itertools.chain(*self.pages.values()))) == 1:
return html.Div(hidden=True, id="nav_panel_outer")
return html.Div(hidden=True, id="nav-panel")

accordion_items = []
for page_group, page_members in self.pages.items():
Expand All @@ -67,8 +67,7 @@ def build(self, *, active_page_id=None):
always_open=True,
),
],
className="nav_panel",
id="nav_panel_outer",
id="nav-panel",
)

def _create_accordion_buttons(self, pages, active_page_id):
Expand Down
10 changes: 5 additions & 5 deletions vizro-core/src/vizro/models/_navigation/nav_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ def pre_build(self):
@_log_call
def build(self, *, active_page_id=None) -> _NavBuildType:
# We always show all the nav_link buttons, but only show the accordion for the active page. This works because
# item.build only returns the nav_panel_outer Div when the item is active.
# item.build only returns the nav_panel Div when the item is active.
# In future maybe we should do this by showing all navigation panels and then setting hidden=True for all but
# one using a clientside callback?
# Wrapping built_items into html.Div here is not for rendering purposes but so that we can look up the
# components by id easily instead of needing to iterate through a nested list.
built_items = html.Div([item.build(active_page_id=active_page_id) for item in self.items])
buttons = [built_items[item.id] for item in self.items]
if "nav_panel_outer" in built_items:
nav_panel_outer = built_items["nav_panel_outer"]
if "nav-panel" in built_items:
nav_panel = built_items["nav-panel"]
else:
# Active page is not in navigation at all, so hide navigation panel.
nav_panel_outer = html.Div(hidden=True, id="nav_panel_outer")
nav_panel = html.Div(hidden=True, id="nav-panel")

return html.Div([html.Div(buttons, className="nav-bar", id="nav_bar_outer"), nav_panel_outer])
return html.Div([html.Div(buttons, id="nav-bar"), nav_panel])
2 changes: 1 addition & 1 deletion vizro-core/src/vizro/models/_navigation/nav_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def build(self, *, active_page_id=None):
active=item_active,
)

# Only build the nav_selector (id="nav_panel_outer") if the item is active.
# Only build the nav_selector (id="nav-panel") if the item is active.
if item_active:
return html.Div([button, self._nav_selector.build(active_page_id=active_page_id)])

Expand Down
8 changes: 4 additions & 4 deletions vizro-core/src/vizro/models/_navigation/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def pre_build(self):
def build(self, *, active_page_id=None) -> _NavBuildType:
nav_selector = self.nav_selector.build(active_page_id=active_page_id)

if "nav_bar_outer" not in nav_selector:
# e.g. nav_selector is Accordion and nav_selector.build returns single html.Div with id="nav_panel_outer".
if "nav-bar" not in nav_selector:
# e.g. nav_selector is Accordion and nav_selector.build returns single html.Div with id="nav-panel".
# This will make it match the case e.g. nav_selector is NavBar and nav_selector.build returns html.Div
# containing children with id="nav_bar_outer" and id="nav_panel_outer"
nav_selector = html.Div([html.Div(hidden=True, id="nav_bar_outer"), nav_selector])
# containing children with id="nav-bar" and id="nav-panel"
nav_selector = html.Div([html.Div(hidden=True, id="nav-bar"), nav_selector])

return nav_selector
12 changes: 4 additions & 8 deletions vizro-core/src/vizro/models/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@

from .types import ComponentType, ControlType


# This is just used for type checking. Ideally it would inherit from some dash.development.base_component.Component
# (e.g. html.Div) as well as TypedDict, but that's not possible, and Dash does not have typing support anyway. When
# this type is used, the object is actually still a dash.development.base_component.Component, but this makes it easier
# to see what contract the component fulfills by making the expected keys explicit.
class _PageBuildType(TypedDict):
control_panel_outer: html.Div
component_container_outer: html.Div
_PageBuildType = TypedDict("_PageBuildType", {"control-panel": html.Div, "components": html.Div})


class Page(VizroBaseModel):
Expand Down Expand Up @@ -128,9 +125,9 @@ def build(self) -> _PageBuildType:
self._update_graph_theme()
controls_content = [control.build() for control in self.controls]
control_panel = (
html.Div(children=[*controls_content], className="control_panel", id="control_panel_outer")
html.Div(children=[*controls_content], id="control-panel")
if controls_content
else html.Div(hidden=True, id="control_panel_outer")
else html.Div(hidden=True, id="control-panel")
)
components_content = [
html.Div(
Expand Down Expand Up @@ -187,7 +184,6 @@ def _create_component_container(self, components_content):
),
dcc.Store(id=f"{ON_PAGE_LOAD_ACTION_PREFIX}_trigger_{self.id}"),
],
className="component_container",
id="component_container_outer",
id="components",
)
return component_container
12 changes: 6 additions & 6 deletions vizro-core/src/vizro/static/css/accordion.css
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
width: 100%;
}

div.page_container .accordion-item-button {
#page-container .accordion-item-button {
huong-li-nguyen marked this conversation as resolved.
Show resolved Hide resolved
align-items: center;
background-color: inherit;
color: var(--text-secondary);
Expand All @@ -116,7 +116,7 @@ div.page_container .accordion-item-button {
width: 100%;
}

.accordion-item-button.btn.btn-primary.active {
#page-container .accordion-item-button.btn.btn-primary.active {
background-color: var(--state-overlays-selected);
color: var(--text-primary);
}
Expand All @@ -125,25 +125,25 @@ div.page_container .accordion-item-button {
* Since the text inside a accordion btn is very specific
* Adding important to increase the specificity
*/
.accordion-item-button a {
#page-container .accordion-item-button a {
align-items: flex-start;
display: flex;
font-size: var(--text-size-02) !important;
line-height: var(--text-size-03) !important;
width: 100%;
}

div.page_container .accordion-item-button:focus {
#page-container .accordion-item-button:focus {
background-color: var(--background-selected);
color: var(--text-primary);
}

div.page_container .accordion-item-button:hover {
#page-container .accordion-item-button:hover {
background-color: var(--state-overlays-selected-hover);
color: var(--text-primary);
}

div.page_container .accordion-button:hover {
#page-container .accordion-button:hover {
color: var(--text-primary);
}

Expand Down
Loading
Loading