Skip to content

Commit

Permalink
Add in-app guide mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
edan-bainglass committed Nov 18, 2024
1 parent 4380a7b commit 1b8e692
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 9 deletions.
3 changes: 3 additions & 0 deletions src/aiidalab_qe/app/static/styles/infobox.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
.info-box p {
line-height: 24px;
}
.info-box.in-app-guide.show {
display: flex !important;
}
3 changes: 3 additions & 0 deletions src/aiidalab_qe/app/static/templates/guide.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@
For a more in-depth dive into the app's features, please refer to the
<a href="https://aiidalab-qe.readthedocs.io/howto/index.html" target="_blank">how-to guides</a>.
</p>

<p>
Alternatively, you can select one of our in-app guides below to walk through an example use-case.
</p>
</div>
37 changes: 35 additions & 2 deletions src/aiidalab_qe/app/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ipywidgets as ipw
import traitlets
from IPython.display import Javascript, display

from aiidalab_qe.common.widgets import LoadingWidget

Expand Down Expand Up @@ -56,7 +57,10 @@ def enable_toggles(self) -> None:
def _on_guide_toggle(self, change: dict):
"""Toggle the guide section."""
if change["new"]:
self._view.info_container.children = [self._view.guide]
self._view.info_container.children = [
self._view.guide,
self._view.guide_selection,
]
self._view.info_container.layout.display = "flex"
self._view.job_history_toggle.value = False
else:
Expand Down Expand Up @@ -89,11 +93,34 @@ def _on_job_history_toggle(self, change: dict):
else:
self._view.main.children = self._old_view

def _on_guide_select(self, change: dict):
"""Toggle the guide section."""
display(
Javascript(f"""
document.querySelectorAll('.{change["old"]}-guide-identifier').forEach(
(guide) => {'{'}
guide.classList.remove('show');
{'}'}
);
""")
)
if (guide_class := change["new"]) != "none":
display(
Javascript(f"""
document.querySelectorAll('.{guide_class}-guide-identifier').forEach(
(guide) => {'{'}
guide.classList.add('show');
{'}'}
);
""")
)

def _set_event_handlers(self) -> None:
"""Set up event handlers."""
self._view.guide_toggle.observe(self._on_guide_toggle, "value")
self._view.about_toggle.observe(self._on_about_toggle, "value")
self._view.job_history_toggle.observe(self._on_job_history_toggle, "value")
self._view.guide_selection.observe(self._on_guide_select, "value")


class AppWrapperModel(traitlets.HasTraits):
Expand All @@ -114,7 +141,7 @@ def __init__(self) -> None:
from datetime import datetime

from importlib_resources import files
from IPython.display import Image, display
from IPython.display import Image
from jinja2 import Environment

from aiidalab_qe.app.static import templates
Expand Down Expand Up @@ -184,6 +211,12 @@ def __init__(self) -> None:
self.guide = ipw.HTML(env.from_string(guide_template).render())
self.about = ipw.HTML(env.from_string(about_template).render())

self.guide_selection = ipw.RadioButtons(
options=["none", "basic", "advanced"],
description="Guides:",
value="none",
)

self.job_history = QueryInterface()

self.info_container = InfoBox()
Expand Down
18 changes: 18 additions & 0 deletions src/aiidalab_qe/common/infobox.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,21 @@ def __init__(self, classes: list[str] | None = None, **kwargs):
for custom_class in custom_classes.split(" "):
if custom_class:
self.add_class(custom_class)


class InAppGuide(InfoBox):
"""The `InfoAppGuide` is used to set up in-app guides that may be toggle in unison."""

def __init__(self, guide_class: str, classes: list[str] | None = None, **kwargs):
"""`InAppGuide` constructor.
Parameters
----------
`guide_class` : `str`
The unique identifier for the guide.
`classes` : `list[str]`, optional
One or more CSS classes.
"""

classes = ["in-app-guide", *(classes or []), f"{guide_class}-guide-identifier"]
super().__init__(classes=classes, **kwargs)
16 changes: 15 additions & 1 deletion tests/test_infobox.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from aiidalab_qe.common.infobox import InfoBox
from aiidalab_qe.common.infobox import InAppGuide, InfoBox


def test_infobox_classes():
Expand All @@ -14,3 +14,17 @@ def test_infobox_classes():
"custom-3",
)
)


def test_in_app_guide():
"""Test `InAppGuide` class."""
guide_class = "some_guide"
in_app_guide = InAppGuide(guide_class=guide_class)
assert all(
css_class in in_app_guide._dom_classes
for css_class in (
"info-box",
"in-app-guide",
f"{guide_class}-guide-identifier",
)
)
12 changes: 6 additions & 6 deletions tests/test_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_guide_toggle(self):
self.controller._on_guide_toggle({"new": True})
self._assert_guide_is_on()
self.controller._on_guide_toggle({"new": False})
self._assert_no_guide_info()
self._assert_no_info()

def test_about_toggle(self):
"""Test about_toggle method."""
Expand All @@ -27,33 +27,33 @@ def test_about_toggle(self):
self.controller._on_about_toggle({"new": True})
self._assert_about_is_on()
self.controller._on_about_toggle({"new": False})
self._assert_no_guide_info()
self._assert_no_info()

def test_toggle_switch(self):
"""Test toggle_switch method."""
self._instansiate_mvc_components()
self.controller.enable_toggles()
self._assert_no_guide_info()
self._assert_no_info()
self.controller._on_guide_toggle({"new": True})
self._assert_guide_is_on()
self.controller._on_about_toggle({"new": True})
self._assert_about_is_on()
self.controller._on_guide_toggle({"new": True})
self._assert_guide_is_on()
self.controller._on_guide_toggle({"new": False})
self._assert_no_guide_info()
self._assert_no_info()

def _assert_guide_is_on(self):
"""Assert guide is on."""
assert len(self.view.info_container.children) == 1
assert len(self.view.info_container.children) == 2
assert self.view.guide in self.view.info_container.children

def _assert_about_is_on(self):
"""Assert about is on."""
assert len(self.view.info_container.children) == 1
assert self.view.about in self.view.info_container.children

def _assert_no_guide_info(self):
def _assert_no_info(self):
"""Assert no info is shown."""
assert len(self.view.info_container.children) == 0

Expand Down

0 comments on commit 1b8e692

Please sign in to comment.