C-Dash: Change the order of items in control panel
[csit.git] / csit.infra.dash / app / cdash / trending / layout.py
index 3a7cd94..66aa1d1 100644 (file)
@@ -142,39 +142,34 @@ class Layout:
 
             if tbs.get(dut, None) is None:
                 tbs[dut] = dict()
-            if tbs[dut].get(infra, None) is None:
-                tbs[dut][infra] = dict()
-            if tbs[dut][infra].get(area, None) is None:
-                tbs[dut][infra][area] = dict()
-            if tbs[dut][infra][area].get(test, None) is None:
-                tbs[dut][infra][area][test] = dict()
-                tbs[dut][infra][area][test]["core"] = list()
-                tbs[dut][infra][area][test]["frame-size"] = list()
-                tbs[dut][infra][area][test]["test-type"] = list()
-            if core.upper() not in tbs[dut][infra][area][test]["core"]:
-                tbs[dut][infra][area][test]["core"].append(core.upper())
-            if framesize.upper() not in \
-                    tbs[dut][infra][area][test]["frame-size"]:
-                tbs[dut][infra][area][test]["frame-size"].append(
-                    framesize.upper()
-                )
+            if tbs[dut].get(area, None) is None:
+                tbs[dut][area] = dict()
+            if tbs[dut][area].get(test, None) is None:
+                tbs[dut][area][test] = dict()
+            if tbs[dut][area][test].get(infra, None) is None:
+                tbs[dut][area][test][infra] = {
+                    "core": list(),
+                    "frame-size": list(),
+                    "test-type": list()
+                }
+            tst_params = tbs[dut][area][test][infra]
+            if core.upper() not in tst_params["core"]:
+                tst_params["core"].append(core.upper())
+            if framesize.upper() not in tst_params["frame-size"]:
+                tst_params["frame-size"].append(framesize.upper())
             if row["test_type"] == "mrr":
-                if "MRR" not in tbs[dut][infra][area][test]["test-type"]:
-                    tbs[dut][infra][area][test]["test-type"].append("MRR")
+                if "MRR" not in tst_params["test-type"]:
+                    tst_params["test-type"].append("MRR")
             elif row["test_type"] == "ndrpdr":
-                if "NDR" not in tbs[dut][infra][area][test]["test-type"]:
-                    tbs[dut][infra][area][test]["test-type"].extend(
-                        ("NDR", "PDR")
-                    )
+                if "NDR" not in tst_params["test-type"]:
+                    tst_params["test-type"].extend(("NDR", "PDR"))
             elif row["test_type"] == "hoststack":
                 if row["tg_type"] in ("iperf", "vpp"):
-                    if "BPS" not in tbs[dut][infra][area][test]["test-type"]:
-                        tbs[dut][infra][area][test]["test-type"].append("BPS")
+                    if "BPS" not in tst_params["test-type"]:
+                        tst_params["test-type"].append("BPS")
                 elif row["tg_type"] == "ab":
-                    if "CPS" not in tbs[dut][infra][area][test]["test-type"]:
-                        tbs[dut][infra][area][test]["test-type"].extend(
-                            ("CPS", "RPS")
-                        )
+                    if "CPS" not in tst_params["test-type"]:
+                        tst_params["test-type"].extend(("CPS", "RPS"))
         self._spec_tbs = tbs
 
         # Read from files:
@@ -265,7 +260,7 @@ class Layout:
                         dbc.Offcanvas(
                             class_name="w-50",
                             id="offcanvas-metadata",
-                            title="Throughput And Latency",
+                            title="Detailed Information",
                             placement="end",
                             is_open=False,
                             children=[
@@ -274,20 +269,25 @@ class Layout:
                             ]
                         ),
                         delay_show=C.SPINNER_DELAY
+                    ),
+                    dbc.Offcanvas(
+                        class_name="w-75",
+                        id="offcanvas-documentation",
+                        title="Documentation",
+                        placement="end",
+                        is_open=False,
+                        children=html.Iframe(
+                            src=C.URL_DOC_TRENDING,
+                            width="100%",
+                            height="100%"
+                        )
                     )
                 ]
             )
         else:
             return html.Div(
-                id="div-main-error",
-                children=[
-                    dbc.Alert(
-                        [
-                            "An Error Occured"
-                        ],
-                        color="danger"
-                    )
-                ]
+                dbc.Alert("An Error Occured", color="danger"),
+                id="div-main-error"
             )
 
     def _add_navbar(self):
@@ -297,17 +297,29 @@ class Layout:
         :rtype: dbc.NavbarSimple
         """
         return dbc.NavbarSimple(
-            id="navbarsimple-main",
             children=[
-                dbc.NavItem(
-                    dbc.NavLink(
-                        C.TREND_TITLE,
-                        disabled=True,
-                        external_link=True,
-                        href="#"
-                    )
-                )
+                dbc.NavItem(dbc.NavLink(
+                    C.TREND_TITLE,
+                    active=True,
+                    external_link=True,
+                    href="/trending"
+                )),
+                dbc.NavItem(dbc.NavLink(
+                    C.NEWS_TITLE,
+                    external_link=True,
+                    href="/news"
+                )),
+                dbc.NavItem(dbc.NavLink(
+                    C.STATS_TITLE,
+                    external_link=True,
+                    href="/stats"
+                )),
+                dbc.NavItem(dbc.NavLink(
+                    "Documentation",
+                    id="btn-documentation",
+                ))
             ],
+            id="navbarsimple-main",
             brand=C.BRAND,
             brand_href="/",
             brand_external_link=True,
@@ -321,12 +333,7 @@ class Layout:
         :returns: Column with the control panel.
         :rtype: dbc.Col
         """
-        return dbc.Col([
-            html.Div(
-                children=self._add_ctrl_panel(),
-                className="sticky-top"
-            )
-        ])
+        return dbc.Col(html.Div(self._add_ctrl_panel(), className="sticky-top"))
 
     def _add_ctrl_panel(self) -> list:
         """Add control panel.
@@ -336,326 +343,232 @@ class Layout:
         """
         return [
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-dut",
-                                    "DUT"
-                                )
-                            ),
-                            dbc.Select(
-                                id={"type": "ctrl-dd", "index": "dut"},
-                                placeholder="Select a Device under Test...",
-                                options=sorted(
-                                    [
-                                        {"label": k, "value": k} \
-                                            for k in self._spec_tbs.keys()
-                                    ],
-                                    key=lambda d: d["label"]
-                                )
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(
+                            show_tooltip(self._tooltips, "help-dut", "DUT")
+                        ),
+                        dbc.Select(
+                            id={"type": "ctrl-dd", "index": "dut"},
+                            placeholder="Select a Device under Test...",
+                            options=sorted(
+                                [
+                                    {"label": k, "value": k} \
+                                        for k in self._spec_tbs.keys()
+                                ],
+                                key=lambda d: d["label"]
                             )
-                        ],
-                        size="sm"
-                    )
-                ]
+                        )
+                    ],
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-infra",
-                                    "Infra"
-                                )
-                            ),
-                            dbc.Select(
-                                id={"type": "ctrl-dd", "index": "phy"},
-                                placeholder=\
-                                    "Select a Physical Test Bed Topology..."
-                            )
-                        ],
-                        size="sm"
-                    )
-                ]
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(
+                            show_tooltip(self._tooltips, "help-area", "Area")
+                        ),
+                        dbc.Select(
+                            id={"type": "ctrl-dd", "index": "area"},
+                            placeholder="Select an Area..."
+                        )
+                    ],
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-area",
-                                    "Area"
-                                )
-                            ),
-                            dbc.Select(
-                                id={"type": "ctrl-dd", "index": "area"},
-                                placeholder="Select an Area..."
-                            )
-                        ],
-                        size="sm"
-                    )
-                ]
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(
+                            show_tooltip(self._tooltips, "help-test", "Test")
+                        ),
+                        dbc.Select(
+                            id={"type": "ctrl-dd", "index": "test"},
+                            placeholder="Select a Test..."
+                        )
+                    ],
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-test",
-                                    "Test"
-                                )
-                            ),
-                            dbc.Select(
-                                id={"type": "ctrl-dd", "index": "test"},
-                                placeholder="Select a Test..."
-                            )
-                        ],
-                        size="sm"
-                    )
-                ]
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(
+                            show_tooltip(self._tooltips, "help-infra", "Infra")
+                        ),
+                        dbc.Select(
+                            id={"type": "ctrl-dd", "index": "phy"},
+                            placeholder="Select a Physical Test Bed Topology..."
+                        )
+                    ],
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-framesize",
-                                    "Frame Size"
-                                )
-                            ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "frmsize-all"
-                                        },
-                                        options=C.CL_ALL_DISABLED,
-                                        inline=True,
-                                        class_name="ms-2"
-                                    )
-                                ],
-                                width=2
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(show_tooltip(
+                            self._tooltips,
+                            "help-framesize",
+                            "Frame Size"
+                        )),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "frmsize-all"},
+                                options=C.CL_ALL_DISABLED,
+                                inline=True,
+                                class_name="ms-2"
                             ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "frmsize"
-                                        },
-                                        inline=True
-                                    )
-                                ]
+                            width=2
+                        ),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "frmsize"},
+                                inline=True
                             )
-                        ],
-                        style={"align-items": "center"},
-                        size="sm"
-                    )
-                ]
+                        )
+                    ],
+                    style={"align-items": "center"},
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-cores",
-                                    "Number of Cores"
-                                )
-                            ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "core-all"
-                                        },
-                                        options=C.CL_ALL_DISABLED,
-                                        inline=True,
-                                        class_name="ms-2"
-                                    )
-                                ],
-                                width=2
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(show_tooltip(
+                            self._tooltips,
+                            "help-cores",
+                            "Number of Cores"
+                        )),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "core-all"},
+                                options=C.CL_ALL_DISABLED,
+                                inline=True,
+                                class_name="ms-2"
                             ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "core"
-                                        },
-                                        inline=True
-                                    )
-                                ]
+                            width=2
+                        ),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "core"},
+                                inline=True
                             )
-                        ],
-                        style={"align-items": "center"},
-                        size="sm"
-                    )
-                ]
+                        )
+                    ],
+                    style={"align-items": "center"},
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-ttype",
-                                    "Test Type"
-                                )
-                            ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "tsttype-all"
-                                        },
-                                        options=C.CL_ALL_DISABLED,
-                                        inline=True,
-                                        class_name="ms-2"
-                                    )
-                                ],
-                                width=2
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(show_tooltip(
+                            self._tooltips,
+                            "help-ttype",
+                            "Test Type"
+                        )),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "tsttype-all"},
+                                options=C.CL_ALL_DISABLED,
+                                inline=True,
+                                class_name="ms-2"
                             ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id={
-                                            "type": "ctrl-cl",
-                                            "index": "tsttype"
-                                        },
-                                        inline=True
-                                    )
-                                ]
+                            width=2
+                        ),
+                        dbc.Col(
+                            dbc.Checklist(
+                                id={"type": "ctrl-cl", "index": "tsttype"},
+                                inline=True
                             )
-                        ],
-                        style={"align-items": "center"},
-                        size="sm"
-                    )
-                ]
+                        )
+                    ],
+                    style={"align-items": "center"},
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.InputGroup(
-                        [
-                            dbc.InputGroupText(
-                                children=show_tooltip(
-                                    self._tooltips,
-                                    "help-normalize",
-                                    "Normalization"
-                                )
-                            ),
-                            dbc.Col(
-                                children=[
-                                    dbc.Checklist(
-                                        id="normalize",
-                                        options=[{
-                                            "value": "normalize",
-                                            "label": (
-                                                "Normalize to CPU frequency "
-                                                "2GHz"
-                                            )
-                                        }],
-                                        value=[],
-                                        inline=True,
-                                        class_name="ms-2"
-                                    )
-                                ]
-                            )
-                        ],
-                        style={"align-items": "center"},
-                        size="sm"
-                    )
-                ]
+                dbc.InputGroup(
+                    [
+                        dbc.InputGroupText(show_tooltip(
+                            self._tooltips,
+                            "help-normalize",
+                            "Normalization"
+                        )),
+                        dbc.Col(dbc.Checklist(
+                            id="normalize",
+                            options=[{
+                                "value": "normalize",
+                                "label": "Normalize to CPU frequency 2GHz"
+                            }],
+                            value=[],
+                            inline=True,
+                            class_name="ms-2"
+                        ))
+                    ],
+                    style={"align-items": "center"},
+                    size="sm"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.Button(
-                        id={"type": "ctrl-btn", "index": "add-test"},
-                        children="Add Selected",
-                        color="info"
-                    )
-                ]
+                dbc.Button(
+                    id={"type": "ctrl-btn", "index": "add-test"},
+                    children="Add Selected",
+                    color="info"
+                ),
+                class_name="g-0 p-1"
             ),
             dbc.Row(
+                dbc.ListGroup(
+                    class_name="overflow-auto p-0",
+                    id="lg-selected",
+                    children=[],
+                    style={"max-height": "20em"},
+                    flush=True
+                ),
                 id="row-card-sel-tests",
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
-                children=[
-                    dbc.ListGroup(
-                        class_name="overflow-auto p-0",
-                        id="lg-selected",
-                        children=[],
-                        style={"max-height": "20em"},
-                        flush=True
-                    )
-                ]
             ),
             dbc.Row(
+                dbc.ButtonGroup([
+                    dbc.Button(
+                        "Remove Selected",
+                        id={"type": "ctrl-btn", "index": "rm-test"},
+                        class_name="w-100",
+                        color="info",
+                        disabled=False
+                    ),
+                    dbc.Button(
+                        "Remove All",
+                        id={"type": "ctrl-btn", "index": "rm-test-all"},
+                        class_name="w-100",
+                        color="info",
+                        disabled=False
+                    )
+                ]),
                 id="row-btns-sel-tests",
                 class_name="g-0 p-1",
                 style=C.STYLE_DISABLED,
-                children=[
-                    dbc.ButtonGroup(
-                        children=[
-                            dbc.Button(
-                                id={"type": "ctrl-btn", "index": "rm-test"},
-                                children="Remove Selected",
-                                class_name="w-100",
-                                color="info",
-                                disabled=False
-                            ),
-                            dbc.Button(
-                                id={"type": "ctrl-btn", "index": "rm-test-all"},
-                                children="Remove All",
-                                class_name="w-100",
-                                color="info",
-                                disabled=False
-                            )
-                        ]
-                    )
-                ]
             ),
             dbc.Stack(
-                id="row-btns-add-tm",
-                class_name="g-0 p-1",
-                style=C.STYLE_DISABLED,
-                gap=2,
-                children=[
+                [
                     dbc.Button(
+                        "Add Telemetry Panel",
                         id={"type": "telemetry-btn", "index": "open"},
-                        children="Telemetry",
-                        color="info"
-                    ),
-                    dbc.Button(
-                        id="plot-btn-url",
-                        children="Show URL",
                         color="info"
                     ),
+                    dbc.Button("Show URL", id="plot-btn-url", color="info"),
                     dbc.Modal(
                         [
                             dbc.ModalHeader(dbc.ModalTitle("URL")),
@@ -666,7 +579,11 @@ class Layout:
                         is_open=False,
                         scrollable=True
                     )
-                ]
+                ],
+                id="row-btns-add-tm",
+                class_name="g-0 p-1",
+                style=C.STYLE_DISABLED,
+                gap=2
             )
         ]
 
@@ -679,13 +596,10 @@ class Layout:
         return dbc.Col(
             id="col-plotting-area",
             children=[
-                dbc.Spinner(
-                    dbc.Row(
-                        id="plotting-area-trending",
-                        class_name="g-0 p-0",
-                        children=C.PLACEHOLDER
-                    ),
-                    delay_show=C.SPINNER_DELAY
+                dbc.Row(
+                    id="plotting-area-trending",
+                    class_name="g-0 p-0",
+                    children=C.PLACEHOLDER
                 ),
                 dbc.Row(
                     id="plotting-area-telemetry",
@@ -697,45 +611,50 @@ class Layout:
             style=C.STYLE_DISABLED,
         )
 
-    def _plotting_area_trending(
-            self,
-            tests: list,
-            normalize: bool
-        ) -> dbc.Col:
+    @staticmethod
+    def _plotting_area_trending(graphs: list) -> dbc.Col:
         """Generate the plotting area with all its content.
 
-        :param tests: A list of tests to be displayed in the trending graphs.
-        :param normalize: If True, the data in graphs is normalized.
-        :type tests: list
-        :type normalize: bool
+        :param graphs: A list of graphs to be displayed in the trending page.
+        :type graphs: list
         :returns: A collumn with trending graphs (tput and latency) in tabs.
         :rtype: dbc.Col
         """
-        if not tests:
+        if not graphs:
             return C.PLACEHOLDER
 
-        figs = graph_trending(self._data, tests, self._graph_layout, normalize)
-
-        if not figs[0]:
+        if not graphs[0]:
             return C.PLACEHOLDER
 
         tab_items = [
             dbc.Tab(
                 children=dcc.Graph(
                     id={"type": "graph", "index": "tput"},
-                    figure=figs[0]
+                    figure=graphs[0]
                 ),
                 label="Throughput",
                 tab_id="tab-tput"
             )
         ]
 
-        if figs[1]:
+        if graphs[1]:
+            tab_items.append(
+                dbc.Tab(
+                    children=dcc.Graph(
+                        id={"type": "graph", "index": "bandwidth"},
+                        figure=graphs[1]
+                    ),
+                    label="Bandwidth",
+                    tab_id="tab-bandwidth"
+                )
+            )
+
+        if graphs[2]:
             tab_items.append(
                 dbc.Tab(
                     children=dcc.Graph(
                         id={"type": "graph", "index": "lat"},
-                        figure=figs[1]
+                        figure=graphs[2]
                     ),
                     label="Latency",
                     tab_id="tab-lat"
@@ -743,50 +662,40 @@ class Layout:
             )
 
         trending = [
-            dbc.Row(children=[
+            dbc.Row(
                 dbc.Tabs(
                     children=tab_items,
                     id="tabs",
                     active_tab="tab-tput",
-                )
-            ]),
+                ),
+                class_name="g-0 p-0"
+            ),
             dbc.Row(
-                [
-                    dbc.Col([html.Div(
-                        [
-                            dbc.Button(
-                                id="plot-btn-download",
-                                children="Download Data",
-                                class_name="me-1",
-                                color="info",
-                                style={"padding": "0rem 1rem"}
-                            ),
-                            dcc.Download(id="download-trending-data")
-                        ],
-                        className=\
-                            "d-grid gap-0 d-md-flex justify-content-md-end"
-                    )])
-                ],
+                html.Div(
+                    [
+                        dbc.Button(
+                            "Download Data",
+                            id="plot-btn-download",
+                            class_name="me-1",
+                            color="info",
+                            style={"padding": "0rem 1rem"}
+                        ),
+                        dcc.Download(id="download-trending-data")
+                    ],
+                    className="d-grid gap-0 d-md-flex justify-content-md-end"
+                ),
                 class_name="g-0 p-0"
             )
         ]
 
         return dbc.Col(
             children=[
-                dbc.Row(
-                    dbc.Accordion(
-                        children=[
-                            dbc.AccordionItem(
-                                title="Trending",
-                                children=trending
-                            )
-                        ],
-                        class_name="g-0 p-1",
-                        start_collapsed=False,
-                        always_open=True,
-                        active_item=["item-0", ]
-                    ),
-                    class_name="g-0 p-0",
+                dbc.Accordion(
+                    dbc.AccordionItem(trending, title="Trending"),
+                    class_name="g-0 p-1",
+                    start_collapsed=False,
+                    always_open=True,
+                    active_item=["item-0", ]
                 ),
                 dbc.Modal(
                     [
@@ -795,23 +704,26 @@ class Layout:
                             close_button=False
                         ),
                         dbc.Spinner(
-                            dbc.ModalBody(self._get_telemetry_step_1()),
+                            dbc.ModalBody(Layout._get_telemetry_step_1()),
                             delay_show=2 * C.SPINNER_DELAY
                         ),
                         dbc.ModalFooter([
                             dbc.Button(
                                 "Select",
                                 id={"type": "telemetry-btn", "index": "select"},
+                                color="success",
                                 disabled=True
                             ),
                             dbc.Button(
                                 "Cancel",
                                 id={"type": "telemetry-btn", "index": "cancel"},
+                                color="info",
                                 disabled=False
                             ),
                             dbc.Button(
-                                "Remove",
-                                id={"type": "telemetry-btn", "index": "remove"},
+                                "Remove All",
+                                id={"type": "telemetry-btn", "index": "rm-all"},
+                                color="danger",
                                 disabled=False
                             )
                         ])
@@ -830,23 +742,26 @@ class Layout:
                             close_button=False
                         ),
                         dbc.Spinner(
-                            dbc.ModalBody(self._get_telemetry_step_2()),
+                            dbc.ModalBody(Layout._get_telemetry_step_2()),
                             delay_show=2 * C.SPINNER_DELAY
                         ),
                         dbc.ModalFooter([
                             dbc.Button(
                                 "Back",
                                 id={"type": "telemetry-btn", "index": "back"},
+                                color="info",
                                 disabled=False
                             ),
                             dbc.Button(
-                                "Display Telemetry",
+                                "Add Telemetry Panel",
                                 id={"type": "telemetry-btn", "index": "add"},
+                                color="success",
                                 disabled=True
                             ),
                             dbc.Button(
                                 "Cancel",
                                 id={"type": "telemetry-btn", "index": "cancel"},
+                                color="info",
                                 disabled=False
                             )
                         ])
@@ -861,37 +776,94 @@ class Layout:
             ]
         )
 
-    def _plotting_area_telemetry(self, graphs: list) -> dbc.Col:
+    @staticmethod
+    def _plotting_area_telemetry(graphs: list) -> dbc.Col:
         """Generate the plotting area with telemetry.
+
+        :param graphs: A list of graphs to be displayed in the telemetry page.
+        :type graphs: list
+        :returns: A collumn with telemetry trending graphs.
+        :rtype: dbc.Col
         """
         if not graphs:
             return C.PLACEHOLDER
 
-        acc_items = list()
-        for graph in graphs:
-            acc_items.append(
+        def _plural(iterative):
+            return "s" if len(iterative) > 1 else str()
+
+        panels = list()
+        for idx, graph_set in enumerate(graphs):
+            acc_items = list()
+            for graph in graph_set[0]:
+                graph_name = ", ".join(graph[1])
+                acc_items.append(
+                    dbc.AccordionItem(
+                        dcc.Graph(
+                            id={"type": "graph-telemetry", "index": graph_name},
+                            figure=graph[0]
+                        ),
+                        title=(f"Test{_plural(graph[1])}: {graph_name}"),
+                        class_name="g-0 p-0"
+                    )
+                )
+            panels.append(
                 dbc.AccordionItem(
-                    title=f"Telemetry: {graph[1]}" if graph[1] else "Telemetry",
-                    children=dcc.Graph(
-                        id={"type": "graph-telemetry", "index": graph[1]},
-                        figure=graph[0]
+                    [
+                        dbc.Row(
+                            dbc.Accordion(
+                                children=acc_items,
+                                class_name="g-0 p-0",
+                                start_collapsed=True,
+                                always_open=True,
+                                flush=True
+                            ),
+                            class_name="g-0 p-0"
+                        ),
+                        dbc.Row(
+                            html.Div(
+                                [
+                                    dbc.Button(
+                                        "Remove",
+                                        id={
+                                            "type": "tm-btn-remove",
+                                            "index": idx
+                                        },
+                                        class_name="me-1",
+                                        color="danger",
+                                        style={"padding": "0rem 1rem"}
+                                    ),
+                                    dbc.Button(
+                                        "Download Data",
+                                        id={
+                                            "type": "tm-btn-download",
+                                            "index": idx
+                                        },
+                                        class_name="me-1",
+                                        color="info",
+                                        style={"padding": "0rem 1rem"}
+                                    )
+                                ],
+                            className=\
+                                "d-grid gap-0 d-md-flex justify-content-md-end"
+                            ),
+                            class_name="g-0 p-0"
+                        )
+                    ],
+                    class_name="g-0 p-0",
+                    title=(
+                        f"Metric{_plural(graph_set[1])}: ",
+                        ", ".join(graph_set[1])
                     )
                 )
             )
 
         return dbc.Col(
-            children=[
-                dbc.Row(
-                    dbc.Accordion(
-                        children=acc_items,
-                        class_name="g-0 p-1",
-                        start_collapsed=False,
-                        always_open=True,
-                        active_item=[f"item-{i}" for i in range(len(acc_items))]
-                    ),
-                    class_name="g-0 p-0",
-                )
-            ]
+            dbc.Accordion(
+                panels,
+                class_name="g-0 p-1",
+                start_collapsed=True,
+                always_open=True
+            )
         )
 
     @staticmethod
@@ -938,30 +910,38 @@ class Layout:
         """
         return [
             dbc.Row(
+                "Add content here.",
                 id={"type": "tm-container", "index": 0},
-                class_name="g-0 p-1",
-                children=["Add content here."]
+                class_name="g-0 p-1"
             ),
             dbc.Row(
-                class_name="g-0 p-2",
-                children=[
-                    dbc.Checkbox(
-                        id={"type": "cb-all-in-one", "index": 0},
-                        label="All Metrics in one Graph"
+                [
+                    dbc.Col(
+                        dbc.Checkbox(
+                            id={"type": "cb-all-in-one", "index": 0},
+                            label="All Metrics in one Graph"
+                        ),
+                        width=6
                     ),
-                ]
+                    dbc.Col(
+                        dbc.Checkbox(
+                            id={"type": "cb-ignore-host", "index": 0},
+                            label="Ignore Host"
+                        ),
+                        width=6
+                    )
+                ],
+                class_name="g-0 p-2"
             ),
             dbc.Row(
-                class_name="g-0 p-1",
-                children=[
-                    dbc.Textarea(
-                        id={"type": "tm-list-metrics", "index": 0},
-                        rows=20,
-                        size="sm",
-                        wrap="off",
-                        readonly=True
-                    )
-                ]
+                dbc.Textarea(
+                    id={"type": "tm-list-metrics", "index": 0},
+                    rows=20,
+                    size="sm",
+                    wrap="off",
+                    readonly=True
+                ),
+                class_name="g-0 p-1"
             )
         ]
 
@@ -1014,19 +994,21 @@ class Layout:
             State("store", "data"),
             State({"type": "sel-cl", "index": ALL}, "value"),
             State({"type": "cb-all-in-one", "index": ALL}, "value"),
+            State({"type": "cb-ignore-host", "index": ALL}, "value"),
             State({"type": "telemetry-search-out", "index": ALL}, "children"),
             State({"type": "plot-mod-telemetry", "index": ALL}, "is_open"),
             State({"type": "telemetry-btn", "index": ALL}, "disabled"),
             State({"type": "tm-container", "index": ALL}, "children"),
             State({"type": "tm-list-metrics", "index": ALL}, "value"),
+            State({"type": "tele-cl", "index": ALL}, "value"),
 
             Input("url", "href"),
-            Input({"type": "tele-cl", "index": ALL}, "value"),
             Input({"type": "tm-dd", "index": ALL}, "value"),
 
             Input("normalize", "value"),
             Input({"type": "telemetry-search-in", "index": ALL}, "value"),
             Input({"type": "telemetry-btn", "index": ALL}, "n_clicks"),
+            Input({"type": "tm-btn-remove", "index": ALL}, "n_clicks"),
             Input({"type": "ctrl-dd", "index": ALL}, "value"),
             Input({"type": "ctrl-cl", "index": ALL}, "value"),
             Input({"type": "ctrl-btn", "index": ALL}, "n_clicks"),
@@ -1037,13 +1019,14 @@ class Layout:
                 store: dict,
                 lst_sel: list,
                 all_in_one: list,
+                ignore_host: list,
                 search_out: list,
                 is_open: list,
                 tm_btns_disabled: list,
                 tm_dd: list,
                 list_metrics: list,
-                href: str,
                 cl_metrics: list,
+                href: str,
                 tm_dd_in: list,
                 *_
             ) -> tuple:
@@ -1054,8 +1037,13 @@ class Layout:
                 store = {
                     "control-panel": dict(),
                     "selected-tests": list(),
+                    "trending-graphs": None,
                     "telemetry-data": dict(),
                     "selected-metrics": dict(),
+                    "telemetry-panels": list(),
+                    "telemetry-all-in-one": list(),
+                    "telemetry-ignore-host": list(),
+                    "telemetry-graphs": list(),
                     "url": str()
                 }
 
@@ -1066,6 +1054,9 @@ class Layout:
             store_sel = store["selected-tests"]
             tm_data = store["telemetry-data"]
             tm_user = store["selected-metrics"]
+            tm_panels = store["telemetry-panels"]
+            tm_all_in_one = store["telemetry-all-in-one"]
+            tm_ignore_host = store["telemetry-ignore-host"]
 
             plotting_area_telemetry = no_update
             on_draw = [False, False]  # 0 --> trending, 1 --> telemetry
@@ -1105,34 +1096,38 @@ class Layout:
                     store_sel = literal_eval(url_params["store_sel"][0])
                     normalize = literal_eval(url_params["norm"][0])
                     telemetry = literal_eval(url_params["telemetry"][0])
-                    all_in_one = literal_eval(url_params["all-in-one"][0])
-                except (KeyError, IndexError, AttributeError):
+                    url_p = url_params.get("all-in-one", ["[[None]]"])
+                    tm_all_in_one = literal_eval(url_p[0])
+                    url_p = url_params.get("ignore-host", ["[[None]]"])
+                    tm_ignore_host = literal_eval(url_p[0])
+                    if not isinstance(telemetry, list):
+                        telemetry = [telemetry, ]
+                except (KeyError, IndexError, AttributeError, ValueError):
                     pass
                 if store_sel:
                     last_test = store_sel[-1]
-                    test = self._spec_tbs[last_test["dut"]][last_test["phy"]]\
-                        [last_test["area"]][last_test["test"]]
+                    test = self._spec_tbs[last_test["dut"]]\
+                        [last_test["area"]][last_test["test"]][last_test["phy"]]
                     ctrl_panel.set({
                         "dd-dut-val": last_test["dut"],
-                        "dd-phy-val": last_test["phy"],
-                        "dd-phy-opt": generate_options(
-                            self._spec_tbs[last_test["dut"]].keys()
-                        ),
-                        "dd-phy-dis": False,
                         "dd-area-val": last_test["area"],
                         "dd-area-opt": [
                             {"label": label(v), "value": v} for v in sorted(
-                                self._spec_tbs[last_test["dut"]]\
-                                    [last_test["phy"]].keys()
-                            )
+                                self._spec_tbs[last_test["dut"]].keys())
                         ],
                         "dd-area-dis": False,
                         "dd-test-val": last_test["test"],
                         "dd-test-opt": generate_options(
-                            self._spec_tbs[last_test["dut"]][last_test["phy"]]\
+                            self._spec_tbs[last_test["dut"]]\
                                 [last_test["area"]].keys()
                         ),
                         "dd-test-dis": False,
+                        "dd-phy-val": last_test["phy"],
+                        "dd-phy-opt": generate_options(
+                            self._spec_tbs[last_test["dut"]][last_test["area"]]\
+                                [last_test["test"]].keys()
+                        ),
+                        "dd-phy-dis": False,
                         "cl-core-opt": generate_options(test["core"]),
                         "cl-core-val": [last_test["core"].upper(), ],
                         "cl-core-all-val": list(),
@@ -1148,38 +1143,41 @@ class Layout:
                         "cl-normalize-val": normalize,
                         "btn-add-dis": False
                     })
+                    store["trending-graphs"] = None
+                    store["telemetry-graphs"] = list()
                     on_draw[0] = True
                     if telemetry:
                         tm = TelemetryData(store_sel)
                         tm.from_dataframe(self._data)
                         tm_data = tm.to_json()
                         tm.from_json(tm_data)
-                        tm_user["selected_metrics_with_labels"] = telemetry
+                        tm_panels = telemetry
                         on_draw[1] = True
             elif trigger.type == "normalize":
                 ctrl_panel.set({"cl-normalize-val": trigger.value})
+                store["trending-graphs"] = None
                 on_draw[0] = True
             elif trigger.type == "ctrl-dd":
                 if trigger.idx == "dut":
                     try:
-                        options = generate_options(
-                            self._spec_tbs[trigger.value].keys()
-                        )
+                        dut = self._spec_tbs[trigger.value]
+                        options = [{"label": label(v), "value": v} \
+                            for v in sorted(dut.keys())]
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
                         "dd-dut-val": trigger.value,
-                        "dd-phy-val": str(),
-                        "dd-phy-opt": options,
-                        "dd-phy-dis": disabled,
                         "dd-area-val": str(),
-                        "dd-area-opt": list(),
-                        "dd-area-dis": True,
+                        "dd-area-opt": options,
+                        "dd-area-dis": disabled,
                         "dd-test-val": str(),
                         "dd-test-opt": list(),
                         "dd-test-dis": True,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1194,24 +1192,23 @@ class Layout:
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
                     })
-                elif trigger.idx == "phy":
+                if trigger.idx == "area":
                     try:
                         dut = ctrl_panel.get("dd-dut-val")
-                        phy = self._spec_tbs[dut][trigger.value]
-                        options = [{"label": label(v), "value": v} \
-                            for v in sorted(phy.keys())]
+                        area = self._spec_tbs[dut][trigger.value]
+                        options = generate_options(area.keys())
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
-                        "dd-phy-val": trigger.value,
-                        "dd-area-val": str(),
-                        "dd-area-opt": options,
-                        "dd-area-dis": disabled,
+                        "dd-area-val": trigger.value,
                         "dd-test-val": str(),
-                        "dd-test-opt": list(),
-                        "dd-test-dis": True,
+                        "dd-test-opt": options,
+                        "dd-test-dis": disabled,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": list(),
+                        "dd-phy-dis": True,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1225,22 +1222,22 @@ class Layout:
                         "cl-tsttype-all-val": list(),
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
-                    })  
-                elif trigger.idx == "area":
+                    })
+                if trigger.idx == "test":
                     try:
                         dut = ctrl_panel.get("dd-dut-val")
-                        phy = ctrl_panel.get("dd-phy-val")
-                        area = self._spec_tbs[dut][phy][trigger.value]
-                        options = generate_options(area.keys())
+                        area = ctrl_panel.get("dd-area-val")
+                        test = self._spec_tbs[dut][area][trigger.value]
+                        options = generate_options(test.keys())
                         disabled = False
                     except KeyError:
                         options = list()
                         disabled = True
                     ctrl_panel.set({
-                        "dd-area-val": trigger.value,
-                        "dd-test-val": str(),
-                        "dd-test-opt": options,
-                        "dd-test-dis": disabled,
+                        "dd-test-val": trigger.value,
+                        "dd-phy-val": str(),
+                        "dd-phy-opt": options,
+                        "dd-phy-dis": disabled,
                         "cl-core-opt": list(),
                         "cl-core-val": list(),
                         "cl-core-all-val": list(),
@@ -1255,25 +1252,25 @@ class Layout:
                         "cl-tsttype-all-opt": C.CL_ALL_DISABLED,
                         "btn-add-dis": True
                     })
-                elif trigger.idx == "test":
+                if trigger.idx == "phy":
                     dut = ctrl_panel.get("dd-dut-val")
-                    phy = ctrl_panel.get("dd-phy-val")
                     area = ctrl_panel.get("dd-area-val")
-                    if all((dut, phy, area, trigger.value, )):
-                        test = self._spec_tbs[dut][phy][area][trigger.value]
+                    test = ctrl_panel.get("dd-test-val")
+                    if all((dut, area, test, trigger.value, )):
+                        phy = self._spec_tbs[dut][area][test][trigger.value]
                         ctrl_panel.set({
-                            "dd-test-val": trigger.value,
-                            "cl-core-opt": generate_options(test["core"]),
+                            "dd-phy-val": trigger.value,
+                            "cl-core-opt": generate_options(phy["core"]),
                             "cl-core-val": list(),
                             "cl-core-all-val": list(),
                             "cl-core-all-opt": C.CL_ALL_ENABLED,
                             "cl-frmsize-opt": \
-                                generate_options(test["frame-size"]),
+                                generate_options(phy["frame-size"]),
                             "cl-frmsize-val": list(),
                             "cl-frmsize-all-val": list(),
                             "cl-frmsize-all-opt": C.CL_ALL_ENABLED,
                             "cl-tsttype-opt": \
-                                generate_options(test["test-type"]),
+                                generate_options(phy["test-type"]),
                             "cl-tsttype-val": list(),
                             "cl-tsttype-all-val": list(),
                             "cl-tsttype-all-opt": C.CL_ALL_ENABLED,
@@ -1295,14 +1292,19 @@ class Layout:
                     f"cl-{param}-val": val_sel,
                     f"cl-{param}-all-val": val_all,
                 })
-                if all((ctrl_panel.get("cl-core-val"), 
+                if all((ctrl_panel.get("cl-core-val"),
                         ctrl_panel.get("cl-frmsize-val"),
                         ctrl_panel.get("cl-tsttype-val"), )):
                     ctrl_panel.set({"btn-add-dis": False})
                 else:
                     ctrl_panel.set({"btn-add-dis": True})
             elif trigger.type == "ctrl-btn":
-                on_draw[0] = True
+                tm_panels = list()
+                tm_all_in_one = list()
+                tm_ignore_host = list()
+                store["trending-graphs"] = None
+                store["telemetry-graphs"] = list()
+                on_draw = [True, True]
                 if trigger.idx == "add-test":
                     dut = ctrl_panel.get("dd-dut-val")
                     phy = ctrl_panel.get("dd-phy-val")
@@ -1360,10 +1362,10 @@ class Layout:
                             "tele-cl", False),
                     )
                     is_open = (True, False)
-                    tm_btns_disabled[1], tm_btns_disabled[5] = True, True
+                    tm_btns_disabled[1], tm_btns_disabled[5] = False, True
                 elif trigger.idx == "select":
-                    tm.from_json(tm_data)
                     if any(cl_metrics):
+                        tm.from_json(tm_data)
                         if not tm_user["selected_metrics"]:
                             tm_user["selected_metrics"] = \
                                 tm_user["unique_metrics"]
@@ -1381,17 +1383,22 @@ class Layout:
                             tm_btns_disabled[4] = False
                         is_open = (False, True)
                     else:
-                        tm_user = None
-                        is_open = (False, False)
+                        is_open = (True, False)
                 elif trigger.idx == "add":
                     tm.from_json(tm_data)
+                    tm_panels.append(tm_user["selected_metrics_with_labels"])
+                    tm_all_in_one.append(all_in_one)
+                    tm_ignore_host.append(ignore_host)
                     is_open = (False, False)
                     tm_btns_disabled[1], tm_btns_disabled[5] = True, True
                     on_draw = [True, True]
                 elif trigger.idx == "cancel":
                     is_open = (False, False)
                     tm_btns_disabled[1], tm_btns_disabled[5] = True, True
-                elif trigger.idx == "remove":
+                elif trigger.idx == "rm-all":
+                    tm_panels = list()
+                    tm_all_in_one = list()
+                    tm_ignore_host = list()
                     tm_user = None
                     is_open = (False, False)
                     tm_btns_disabled[1], tm_btns_disabled[5] = True, True
@@ -1406,12 +1413,6 @@ class Layout:
                     colorize=False
                 ), )
                 is_open = (True, False)
-            elif trigger.type == "tele-cl":
-                if any(cl_metrics):
-                    tm_btns_disabled[1] = False
-                else:
-                    tm_btns_disabled[1] = True
-                is_open = (True, False)
             elif trigger.type == "tm-dd":
                 tm.from_metrics_with_labels(
                     tm_user["unique_metrics_with_labels"]
@@ -1441,33 +1442,63 @@ class Layout:
                     tm_btns_disabled[5] = False
                 else:
                     list_metrics[0] = str()
+            elif trigger.type == "tm-btn-remove":
+                del tm_panels[trigger.idx]
+                del tm_all_in_one[trigger.idx]
+                del tm_ignore_host[trigger.idx]
+                del store["telemetry-graphs"][trigger.idx]
+                tm.from_json(tm_data)
+                on_draw = [True, True]
 
             new_url_params = {
                 "store_sel": store_sel,
                 "norm": ctrl_panel.get("cl-normalize-val")
             }
-            if tm_user and tm_user.get("selected_metrics_with_labels", None):
-                new_url_params["telemetry"] = \
-                    tm_user["selected_metrics_with_labels"]
-                new_url_params["all-in-one"] = all_in_one
+            if tm_panels:
+                new_url_params["telemetry"] = tm_panels
+                new_url_params["all-in-one"] = tm_all_in_one
+                new_url_params["ignore-host"] = tm_ignore_host
 
             if on_draw[0]:  # Trending
                 if store_sel:
                     lg_selected = get_list_group_items(store_sel, "sel-cl")
-                    plotting_area_trending = self._plotting_area_trending(
-                        store_sel,
-                        bool(ctrl_panel.get("cl-normalize-val"))
-                    )
-                    if on_draw[1]:  # Telemetry
-                        plotting_area_telemetry = self._plotting_area_telemetry(
-                            graph_tm_trending(
+                    if store["trending-graphs"]:
+                        graphs = store["trending-graphs"]
+                    else:
+                        graphs = graph_trending(
+                            self._data,
+                            store_sel,
+                            self._graph_layout,
+                            bool(ctrl_panel.get("cl-normalize-val"))
+                        )
+                        if graphs and graphs[0]:
+                            store["trending-graphs"] = graphs
+                    plotting_area_trending = \
+                        Layout._plotting_area_trending(graphs)
+
+                    # Telemetry
+                    start_idx = len(store["telemetry-graphs"])
+                    end_idx = len(tm_panels)
+                    if not end_idx:
+                        plotting_area_telemetry = C.PLACEHOLDER
+                    elif on_draw[1] and (end_idx >= start_idx):
+                        if len(tm_all_in_one) != end_idx:
+                            tm_all_in_one = [[None], ] * end_idx
+                        if len(tm_ignore_host) != end_idx:
+                            tm_ignore_host = [[None], ] * end_idx
+                        for idx in range(start_idx, end_idx):
+                            store["telemetry-graphs"].append(graph_tm_trending(
                                 tm.select_tm_trending_data(
-                                    tm_user["selected_metrics_with_labels"]
+                                    tm_panels[idx],
+                                    ignore_host=bool(tm_ignore_host[idx][0])
                                 ),
                                 self._graph_layout,
-                                False if not all_in_one else all_in_one[0]
+                                bool(tm_all_in_one[idx][0])
+                            ))
+                        plotting_area_telemetry = \
+                            Layout._plotting_area_telemetry(
+                                store["telemetry-graphs"]
                             )
-                        )
                     col_plotting_area = C.STYLE_ENABLED
                     row_card_sel_tests = C.STYLE_ENABLED
                     row_btns_sel_tests = C.STYLE_ENABLED
@@ -1481,6 +1512,9 @@ class Layout:
                     row_btns_add_tm = C.STYLE_DISABLED
                     lg_selected = no_update
                     store_sel = list()
+                    tm_panels = list()
+                    tm_all_in_one = list()
+                    tm_ignore_host = list()
                     tm_user = None
             else:
                 plotting_area_trending = no_update
@@ -1495,6 +1529,9 @@ class Layout:
             store["selected-tests"] = store_sel
             store["telemetry-data"] = tm_data
             store["selected-metrics"] = tm_user
+            store["telemetry-panels"] = tm_panels
+            store["telemetry-all-in-one"] = tm_all_in_one
+            store["telemetry-ignore-host"] = tm_ignore_host
             ret_val = [
                 store,
                 plotting_area_trending,
@@ -1657,7 +1694,14 @@ class Layout:
             trigger = Trigger(callback_context.triggered)
 
             try:
-                idx = 0 if trigger.idx == "tput" else 1
+                if trigger.idx == "tput":
+                    idx = 0
+                elif trigger.idx == "bandwidth":
+                    idx = 1
+                elif trigger.idx == "lat":
+                    idx = 2
+                else:
+                    raise PreventUpdate
                 graph_data = graph_data[idx]["points"][0]
             except (IndexError, KeyError, ValueError, TypeError):
                 raise PreventUpdate
@@ -1665,13 +1709,31 @@ class Layout:
             metadata = no_update
             graph = list()
 
-            children = [
-                dbc.ListGroupItem(
-                    [dbc.Badge(x.split(":")[0]), x.split(": ")[1]]
-                ) for x in graph_data.get("text", "").split("<br>")
-            ]
+            list_group_items = list()
+            for itm in graph_data.get("text", None).split("<br>"):
+                if not itm:
+                    continue
+                lst_itm = itm.split(": ")
+                if lst_itm[0] == "csit-ref":
+                    list_group_item = dbc.ListGroupItem([
+                        dbc.Badge(lst_itm[0]),
+                        html.A(
+                            lst_itm[1],
+                            href=f"{C.URL_JENKINS}{lst_itm[1]}",
+                            target="_blank"
+                        )
+                    ])
+                else:
+                    list_group_item = dbc.ListGroupItem([
+                        dbc.Badge(lst_itm[0]),
+                        lst_itm[1]
+                    ])
+                list_group_items.append(list_group_item)
+
             if trigger.idx == "tput":
                 title = "Throughput"
+            elif trigger.idx == "bandwidth":
+                title = "Bandwidth"
             elif trigger.idx == "lat":
                 title = "Latency"
                 hdrh_data = graph_data.get("customdata", None)
@@ -1680,14 +1742,14 @@ class Layout:
                         class_name="gy-2 p-0",
                         children=[
                             dbc.CardHeader(hdrh_data.pop("name")),
-                            dbc.CardBody(children=[
+                            dbc.CardBody(
                                 dcc.Graph(
                                     id="hdrh-latency-graph",
                                     figure=graph_hdrh_latency(
                                         hdrh_data, self._graph_layout
                                     )
                                 )
-                            ])
+                            )
                         ])
                     ]
             else:
@@ -1706,9 +1768,9 @@ class Layout:
                             title
                         ]),
                         dbc.CardBody(
+                            dbc.ListGroup(list_group_items, flush=True),
                             id="tput-lat-metadata",
                             class_name="p-0",
-                            children=[dbc.ListGroup(children, flush=True), ]
                         )
                     ]
                 )
@@ -1720,9 +1782,10 @@ class Layout:
             Output("download-trending-data", "data"),
             State("store", "data"),
             Input("plot-btn-download", "n_clicks"),
+            Input({"type": "tm-btn-download", "index": ALL}, "n_clicks"),
             prevent_initial_call=True
         )
-        def _download_trending_data(store: list, _) -> dict:
+        def _download_data(store: list, *_) -> dict:
             """Download the data
 
             :param store_sel: List of tests selected by user stored in the
@@ -1737,12 +1800,40 @@ class Layout:
                 raise PreventUpdate
             if not store["selected-tests"]:
                 raise PreventUpdate
-
+            
             df = pd.DataFrame()
-            for itm in store["selected-tests"]:
-                sel_data = select_trending_data(self._data, itm)
-                if sel_data is None:
-                    continue
-                df = pd.concat([df, sel_data], ignore_index=True, copy=False)
+            
+            trigger = Trigger(callback_context.triggered)
+            if not trigger.value:
+                raise PreventUpdate
+            
+            if trigger.type == "plot-btn-download":
+                data = list()
+                for itm in store["selected-tests"]:
+                    sel_data = select_trending_data(self._data, itm)
+                    if sel_data is None:
+                        continue
+                    data.append(sel_data)
+                df = pd.concat(data, ignore_index=True, copy=False)
+                file_name = C.TREND_DOWNLOAD_FILE_NAME
+            elif trigger.type == "tm-btn-download":
+                tm = TelemetryData(store["selected-tests"])
+                tm.from_json(store["telemetry-data"])
+                df = tm.select_tm_trending_data(
+                    store["telemetry-panels"][trigger.idx]
+                )
+                file_name = C.TELEMETRY_DOWNLOAD_FILE_NAME
+            else:
+                raise PreventUpdate
+
+            return dcc.send_data_frame(df.to_csv, file_name)
 
-            return dcc.send_data_frame(df.to_csv, C.TREND_DOWNLOAD_FILE_NAME)
+        @app.callback(
+            Output("offcanvas-documentation", "is_open"),
+            Input("btn-documentation", "n_clicks"),
+            State("offcanvas-documentation", "is_open")
+        )
+        def toggle_offcanvas_documentation(n_clicks, is_open):
+            if n_clicks:
+                return not is_open
+            return is_open