Repository: 12rambau/sepal_ui

Issue:
Interact with navigation drawers
Sometimes is useful to pass some data from the module model to the app environment and so far we do not have this implementation.

We can add two simple methods to the drawers so they can update their state with icons, badges, and so.


Discussion:

Patch:
diff --git a/docs/source/modules/sepal_ui.sepalwidgets.DrawerItem.rst b/docs/source/modules/sepal_ui.sepalwidgets.DrawerItem.rst
index a3280cd3..22b87b44 100644
--- a/docs/source/modules/sepal_ui.sepalwidgets.DrawerItem.rst
+++ b/docs/source/modules/sepal_ui.sepalwidgets.DrawerItem.rst
@@ -7,7 +7,9 @@ sepal\_ui.sepalwidgets.DrawerItem
     
     .. autosummary::
     
-        ~DrawerItem.rt        
+        ~DrawerItem.rt 
+        ~DrawerItem.alert
+        ~DrawerItem.alert_badge
     
     .. rubric:: Methods
     
@@ -15,5 +17,11 @@ sepal\_ui.sepalwidgets.DrawerItem
         :nosignatures:
         
         ~DrawerItem.display_tile
+        ~DrawerItem.add_notif
+        ~DrawerItem.remove_notif
         
-.. automethod:: sepal_ui.sepalwidgets.DrawerItem.display_tile
\ No newline at end of file
+.. automethod:: sepal_ui.sepalwidgets.DrawerItem.display_tile
+
+.. automethod:: sepal_ui.sepalwidgets.DrawerItem.add_notif
+
+.. automethod:: sepal_ui.sepalwidgets.DrawerItem.remove_notif
\ No newline at end of file
diff --git a/sepal_ui/sepalwidgets/app.py b/sepal_ui/sepalwidgets/app.py
index a1aff843..2a87de83 100644
--- a/sepal_ui/sepalwidgets/app.py
+++ b/sepal_ui/sepalwidgets/app.py
@@ -1,3 +1,4 @@
+from traitlets import link, Bool, observe
 from functools import partial
 from datetime import datetime
 
@@ -73,12 +74,29 @@ class DrawerItem(v.ListItem, SepalWidget):
         card (str, optional): the mount_id of tiles in the app
         href (str, optional): the absolute link to an external web page
         kwargs (optional): any parameter from a v.ListItem. If set, '_metadata', 'target', 'link' and 'children' will be overwritten.
+        model (optional): sepalwidget model where is defined the bin_var trait
+        bind_var (optional): required when model is selected. Trait to link with 'alert' self trait parameter
     """
 
     rt = None
     "sw.ResizeTrigger: the trigger to resize maps and other javascript object when jumping from a tile to another"
 
-    def __init__(self, title, icon=None, card=None, href=None, **kwargs):
+    alert = Bool(False).tag(sync=True)
+    "Bool: trait to control visibility of an alert in the drawer item"
+
+    alert_badge = None
+    "v.ListItemAction: red circle to display in the drawer"
+
+    def __init__(
+        self,
+        title,
+        icon=None,
+        card=None,
+        href=None,
+        model=None,
+        bind_var=None,
+        **kwargs
+    ):
 
         # set the resizetrigger
         self.rt = js.rt
@@ -108,6 +126,45 @@ class DrawerItem(v.ListItem, SepalWidget):
         # call the constructor
         super().__init__(**kwargs)
 
+        # cannot be set as a class member because it will be shared with all
+        # the other draweritems.
+        self.alert_badge = v.ListItemAction(
+            children=[v.Icon(children=["fas fa-circle"], x_small=True, color="red")]
+        )
+
+        if model:
+            if not bind_var:
+                raise Exception(
+                    "You have selected a model, you need a trait to bind with drawer."
+                )
+
+            link((model, bind_var), (self, "alert"))
+
+    @observe("alert")
+    def add_notif(self, change):
+        """Add a notification alert to drawer"""
+
+        if change["new"]:
+            if self.alert_badge not in self.children:
+                new_children = self.children[:]
+                new_children.append(self.alert_badge)
+                self.children = new_children
+        else:
+            self.remove_notif()
+
+        return
+
+    def remove_notif(self):
+        """Remove notification alert"""
+
+        if self.alert_badge in self.children:
+            new_children = self.children[:]
+            new_children.remove(self.alert_badge)
+
+            self.children = new_children
+
+        return
+
     def display_tile(self, tiles):
         """
         Display the apropriate tiles when the item is clicked.
@@ -138,6 +195,9 @@ class DrawerItem(v.ListItem, SepalWidget):
         # change the current item status
         self.input_value = True
 
+        # Remove notification
+        self.remove_notif()
+
         return self
 
 


Test patch:
diff --git a/tests/test_DrawerItem.py b/tests/test_DrawerItem.py
index ad26c3a2..0e80ffc9 100644
--- a/tests/test_DrawerItem.py
+++ b/tests/test_DrawerItem.py
@@ -1,3 +1,6 @@
+import pytest
+from sepal_ui.model import Model
+from traitlets import Bool
 import ipyvuetify as v
 
 from sepal_ui import sepalwidgets as sw
@@ -60,3 +63,32 @@ class TestDrawerItem:
                 assert tile.viz is False
 
         return
+
+    @pytest.fixture
+    def model(self):
+        class TestModel(Model):
+            app_ready = Bool(False).tag(sync=True)
+
+        return TestModel()
+
+    def test_add_notif(self, model):
+
+        drawer_item = sw.DrawerItem("title", model=model, bind_var="app_ready")
+
+        model.app_ready = True
+
+        assert drawer_item.alert_badge in drawer_item.children
+
+        model.app_ready = False
+
+        assert drawer_item.alert_badge not in drawer_item.children
+
+    def test_remove_notif(self, model):
+
+        drawer_item = sw.DrawerItem("title", model=model, bind_var="app_ready")
+
+        model.app_ready = True
+
+        drawer_item.remove_notif()
+
+        assert drawer_item.alert_badge not in drawer_item.children