Repository: feder-observatory/stellarphot

Issue:
Fix some issues with the custom widget UI
A few things that @Tanner728 found:

- [x] You can type stuff in nullable fields (e.g. AAVSO code for observatory) even if the "switch" is off if you have hit the "Edit" button
- [x] The "Status" button in the ipyautoui save button bar (the leftmost button) is clickable when editing and it shouldn't be.
- [x] "Edit" button appears if there are no items in a group (e.g. no observatories) and it shouldn't.
- [ ] ~~Might be possible to enter an invalid name (e.g. trailing space) in the form.~~
- [x] The edit button doesn't appear after making a new item.

Discussion:

Patch:
diff --git a/stellarphot/settings/custom_widgets.py b/stellarphot/settings/custom_widgets.py
index ef18099..888d121 100644
--- a/stellarphot/settings/custom_widgets.py
+++ b/stellarphot/settings/custom_widgets.py
@@ -4,6 +4,7 @@
 import ipywidgets as ipw
 import traitlets as tr
 from ipyautoui.autoobject import AutoObject
+from ipyautoui.custom.iterable import ItemBox
 
 from stellarphot.settings import (
     Camera,
@@ -239,8 +240,16 @@ class ChooseOrMakeNew(ipw.VBox):
             State that ``disabled`` should be set to.
         """
 
-        if hasattr(top, "disabled") or isinstance(top, AutoObject):
+        if isinstance(top, AutoObject):
             top.disabled = value
+        elif isinstance(top, ItemBox):
+            if value:
+                # Disabled, so do not show the add/remove buttons
+                top.add_remove_controls = "none"
+            else:
+                # Enabled, so show the add/remove buttons
+                top.add_remove_controls = "add_remove"
+
         try:
             for child in top.children:
                 self._set_disable_state_nested_models(child, value)


Test patch:
diff --git a/stellarphot/settings/tests/test_custom_widgets.py b/stellarphot/settings/tests/test_custom_widgets.py
index 1e4bcd3..690ed9a 100644
--- a/stellarphot/settings/tests/test_custom_widgets.py
+++ b/stellarphot/settings/tests/test_custom_widgets.py
@@ -1,5 +1,6 @@
 import ipywidgets as ipw
 import pytest
+from ipyautoui.custom.iterable import ItemBox, ItemControl
 
 from stellarphot.settings import Camera, Observatory, PassbandMap, SavedSettings
 from stellarphot.settings.custom_widgets import ChooseOrMakeNew, Confirm
@@ -187,6 +188,36 @@ class TestChooseOrMakeNew:
         # The item widget should now have the values of the second observatory
         assert Observatory(**choose_or_make_new._item_widget.value) == observatory2
 
+    def test_passband_map_buttons_are_disabled_or_enabled(self, tmp_path):
+        # When an existing PassbandMap is selected the add/remove buttons
+        # for individual rows should not be displayed.
+        saved = SavedSettings(_testing_path=tmp_path)
+        passband_map = PassbandMap(**DEFAULT_PASSBAND_MAP)
+        saved.add_item(passband_map)
+
+        choose_or_make_new = ChooseOrMakeNew("passband_map", _testing_path=tmp_path)
+
+        # There is no great way to get to the ItemBox widget that contains and controls
+        # the add/remove buttons, so we keep going down through widget children until we
+        # get to an ItemBox and then check that the buttons are disabled.
+        # Recursion is the easiest way to do that, so recurse we will..
+        def find_item_box(top_widget):
+            for kid in top_widget.children:
+                if isinstance(kid, ItemBox):
+                    return kid
+                if hasattr(kid, "children"):
+                    result = find_item_box(kid)
+                    if result:
+                        return result
+
+        item_box = find_item_box(choose_or_make_new)
+        assert item_box.add_remove_controls == ItemControl.none
+
+        # Next, we will click the "Edit" button and check that the buttons are enabled.
+        choose_or_make_new._edit_button.click()
+        item_box = find_item_box(choose_or_make_new)
+        assert item_box.add_remove_controls == ItemControl.add_remove
+
     def test_make_passband_map(self, tmp_path):
         # Make a passband map and save it, then check that it is in the dropdown
         saved = SavedSettings(_testing_path=tmp_path)