refactor: restructure sidebar with chart views, remove placeholder items (Task 7.3)
- Remove non-functional sidebar items: Cost Analysis, Export Data - Remove filter trigger items: Drug/Trust/Directory Selection, Indications - Add Chart Views section: Icicle Chart (active), Sankey Diagram (disabled), Timeline (disabled) - Remove tab row from chart_card.py (chart view selection now in sidebar) - Remove open_drawer callback (sidebar no longer has filter triggers) - Add .sidebar__item--disabled CSS class
This commit is contained in:
@@ -100,6 +100,11 @@ body {
|
||||
color: var(--nhs-blue);
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar__item--disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
.sidebar__item svg { width: 18px; height: 18px; flex-shrink: 0; }
|
||||
.sidebar__icon { width: 18px; height: 18px; flex-shrink: 0; }
|
||||
.sidebar__footer {
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
"""Callbacks for the drug browser drawer: open/close, drug selection, fragment matching, clear."""
|
||||
"""Callbacks for drug selection: fragment matching and clear filters.
|
||||
|
||||
The open_drawer callback was removed in Task 7.3 (sidebar restructure) because
|
||||
the sidebar no longer has filter trigger items. Task 7.4 will replace the drawer
|
||||
with modals opened from the filter bar.
|
||||
"""
|
||||
from dash import Input, Output, State, ctx, no_update, ALL
|
||||
|
||||
|
||||
def register_drawer_callbacks(app):
|
||||
"""Register drawer-related callbacks."""
|
||||
|
||||
@app.callback(
|
||||
Output("drug-drawer", "opened"),
|
||||
Input("sidebar-drug-selection", "n_clicks"),
|
||||
Input("sidebar-indications", "n_clicks"),
|
||||
Input("sidebar-trust-selection", "n_clicks"),
|
||||
prevent_initial_call=True,
|
||||
)
|
||||
def open_drawer(_drug_clicks, _indication_clicks, _trust_clicks):
|
||||
"""Open the drawer when sidebar Drug Selection, Indications, or Trust Selection is clicked."""
|
||||
return True
|
||||
"""Register drug/trust selection callbacks (fragment matching + clear)."""
|
||||
|
||||
@app.callback(
|
||||
Output("all-drugs-chips", "value"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Chart card component — header, tabs, and dcc.Graph for icicle chart."""
|
||||
"""Chart card component — header and dcc.Graph for icicle chart."""
|
||||
from dash import html, dcc
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ def make_chart_card():
|
||||
|
||||
Contains:
|
||||
- Header with title and dynamic subtitle (hierarchy label)
|
||||
- Tab row (Icicle active, Sankey and Timeline as disabled placeholders)
|
||||
- dcc.Loading wrapper around dcc.Graph for loading spinner
|
||||
Chart view selection (icicle/sankey/timeline) is in the sidebar.
|
||||
"""
|
||||
return html.Section(
|
||||
className="chart-card",
|
||||
@@ -33,33 +33,6 @@ def make_chart_card():
|
||||
),
|
||||
],
|
||||
),
|
||||
# Tab row
|
||||
html.Div(
|
||||
className="chart-card__tabs",
|
||||
role="tablist",
|
||||
children=[
|
||||
html.Button(
|
||||
"Icicle",
|
||||
className="chart-tab chart-tab--active",
|
||||
role="tab",
|
||||
**{"aria-selected": "true"},
|
||||
),
|
||||
html.Button(
|
||||
"Sankey",
|
||||
className="chart-tab",
|
||||
role="tab",
|
||||
disabled=True,
|
||||
**{"aria-selected": "false"},
|
||||
),
|
||||
html.Button(
|
||||
"Timeline",
|
||||
className="chart-tab",
|
||||
role="tab",
|
||||
disabled=True,
|
||||
**{"aria-selected": "false"},
|
||||
),
|
||||
],
|
||||
),
|
||||
# Chart area with loading spinner
|
||||
dcc.Loading(
|
||||
type="circle",
|
||||
|
||||
@@ -5,11 +5,7 @@ from dash import html
|
||||
|
||||
|
||||
def _svg_icon(svg_body):
|
||||
"""Wrap an SVG body string into an html.Img using a data URI.
|
||||
|
||||
This avoids needing dash-svg or dangerouslySetInnerHTML.
|
||||
The SVG icons are copied from 01_nhs_classic.html.
|
||||
"""
|
||||
"""Wrap an SVG body string into an html.Img using a data URI."""
|
||||
svg = (
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" '
|
||||
f'fill="none" stroke="currentColor" stroke-width="2">{svg_body}</svg>'
|
||||
@@ -20,15 +16,12 @@ def _svg_icon(svg_body):
|
||||
)
|
||||
|
||||
|
||||
# SVG icon bodies from 01_nhs_classic.html
|
||||
# SVG icon bodies (Feather-style)
|
||||
_ICONS = {
|
||||
"pathway": '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>',
|
||||
"drug": '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/>',
|
||||
"trust": '<path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/>',
|
||||
"directory": '<path d="M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z"/>',
|
||||
"indication": '<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14,2 14,8 20,8"/>',
|
||||
"cost": '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
|
||||
"export": '<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>',
|
||||
"icicle": '<rect x="3" y="3" width="18" height="4" rx="1"/><rect x="3" y="10" width="10" height="4" rx="1"/><rect x="3" y="17" width="6" height="4" rx="1"/>',
|
||||
"sankey": '<path d="M6 3v18"/><path d="M18 3v18"/><path d="M6 8c6 0 6 5 12 5"/><path d="M6 16c4 0 4-3 12-3"/>',
|
||||
"timeline": '<line x1="3" y1="12" x2="21" y2="12"/><circle cx="6" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="18" cy="12" r="2"/>',
|
||||
}
|
||||
|
||||
|
||||
@@ -38,31 +31,22 @@ def make_sidebar():
|
||||
className="sidebar",
|
||||
**{"aria-label": "Main navigation"},
|
||||
children=[
|
||||
# Analysis section
|
||||
# Overview section
|
||||
html.Div(
|
||||
className="sidebar__section",
|
||||
children=[
|
||||
html.Div("Analysis", className="sidebar__label"),
|
||||
html.Div("Overview", className="sidebar__label"),
|
||||
_sidebar_item("Pathway Overview", "pathway", active=True),
|
||||
_sidebar_item(
|
||||
"Drug Selection", "drug", item_id="sidebar-drug-selection"
|
||||
),
|
||||
_sidebar_item(
|
||||
"Trust Selection", "trust", item_id="sidebar-trust-selection"
|
||||
),
|
||||
_sidebar_item("Directory Selection", "directory"),
|
||||
_sidebar_item(
|
||||
"Indications", "indication", item_id="sidebar-indications"
|
||||
),
|
||||
],
|
||||
),
|
||||
# Reports section
|
||||
# Chart views section
|
||||
html.Div(
|
||||
className="sidebar__section",
|
||||
children=[
|
||||
html.Div("Reports", className="sidebar__label"),
|
||||
_sidebar_item("Cost Analysis", "cost"),
|
||||
_sidebar_item("Export Data", "export"),
|
||||
html.Div("Chart Views", className="sidebar__label"),
|
||||
_sidebar_item("Icicle Chart", "icicle", active=True),
|
||||
_sidebar_item("Sankey Diagram", "sankey", disabled=True),
|
||||
_sidebar_item("Timeline", "timeline", disabled=True),
|
||||
],
|
||||
),
|
||||
# Footer
|
||||
@@ -78,11 +62,13 @@ def make_sidebar():
|
||||
)
|
||||
|
||||
|
||||
def _sidebar_item(label, icon_key, active=False, item_id=None):
|
||||
def _sidebar_item(label, icon_key, active=False, disabled=False, item_id=None):
|
||||
"""Create a single sidebar navigation item."""
|
||||
class_name = "sidebar__item"
|
||||
if active:
|
||||
class_name += " sidebar__item--active"
|
||||
if disabled:
|
||||
class_name += " sidebar__item--disabled"
|
||||
|
||||
props = {"className": class_name}
|
||||
if item_id:
|
||||
|
||||
Reference in New Issue
Block a user