diff --git a/datashuttle/tui/tabs/create_folders.py b/datashuttle/tui/tabs/create_folders.py index 2ec76e728..7e6686a23 100644 --- a/datashuttle/tui/tabs/create_folders.py +++ b/datashuttle/tui/tabs/create_folders.py @@ -94,9 +94,8 @@ def compose(self) -> ComposeResult: ) yield Label("Datatype(s)", id="create_folders_datatype_label") yield Container( - DatatypeCheckboxes( - self.interface, id="create_folders_datatype_checkboxes" - ) + self.get_datatype_checkboxes_widget(), + id="create_folders_datatype_container", # ) yield Horizontal( Button( @@ -157,9 +156,17 @@ def on_button_pressed(self, event: Button.Pressed) -> None: ) async def refresh_after_datatypes_changed(self, ignore) -> None: - """Redisplay the datatype checkboxes.""" - await self.recompose() - self.on_mount() + """Redisplay the datatype checkboxes. + + The widget must be completely removed and reinitialised. + """ + container = self.query_one("#create_folders_datatype_container") + + await container.query_one( + "#create_folders_datatype_checkboxes" + ).remove() + + await container.mount(self.get_datatype_checkboxes_widget()) @require_double_click def on_clickable_input_clicked( @@ -534,3 +541,9 @@ def run_local_validation(self, prefix: Prefix) -> tuple[bool, str]: def update_directorytree_root(self, new_root_path: Path) -> None: """Refresh the tree through the reactive attribute `path`.""" self.query_one("#create_folders_directorytree").path = new_root_path + + def get_datatype_checkboxes_widget(self): + """Create the datatype checkboxes, centralised as used in multiple places.""" + return DatatypeCheckboxes( + self.interface, id="create_folders_datatype_checkboxes" + ) diff --git a/datashuttle/tui/tabs/transfer.py b/datashuttle/tui/tabs/transfer.py index 63dd78112..a6362ad85 100644 --- a/datashuttle/tui/tabs/transfer.py +++ b/datashuttle/tui/tabs/transfer.py @@ -141,15 +141,8 @@ def compose(self) -> ComposeResult: ), # These are almost identical to create tab Label("Datatype(s)", id="transfer_datatype_label"), - DatatypeCheckboxes( - self.interface, - create_or_transfer="transfer", - id="transfer_custom_datatype_checkboxes", - ), - Button( - "Displayed Datatypes", - id="transfer_tab_displayed_datatypes_button", - ), + self.get_datatypes_checkboxes_widget(), + self.get_displayed_datatypes_button(), ] yield TransferStatusTree( @@ -325,11 +318,28 @@ def on_button_pressed(self, event: Button.Pressed) -> None: ) async def refresh_after_datatype_changed(self, ignore): - """Refresh Checkboxes after the shown datatypes have changed.""" - await self.recompose() - self.on_mount() - self.query_one("#transfer_custom_radiobutton").value = True - self.switch_transfer_widgets_display() + """Refresh Checkboxes after the shown datatypes have changed. + + The widget must be completely removed and reinitialised. + This means the button underneath it must also be removed and + re-added, or it ends up above the datatype checkbox widget. + """ + container = self.query_one("#transfer_params_container") + await container.query_one( + "#transfer_custom_datatype_checkboxes" + ).remove() + await container.query_one( + "#transfer_tab_displayed_datatypes_button" + ).remove() + + ( + Button( + "Displayed Datatypes", + id="transfer_tab_displayed_datatypes_button", + ), + ) + await container.mount(self.get_datatypes_checkboxes_widget()) + await container.mount(self.get_displayed_datatypes_button()) def on_custom_directory_tree_directory_tree_special_key_press( self, event: CustomDirectoryTree.DirectoryTreeSpecialKeyPress @@ -411,3 +421,18 @@ def transfer_data(self) -> Worker[InterfaceOutput]: self.app.call_from_thread(self.reload_directorytree) return success, output + + def get_datatypes_checkboxes_widget(self): + """Create the datatype checkboxes, centralised as used in multiple places.""" + return DatatypeCheckboxes( + self.interface, + create_or_transfer="transfer", + id="transfer_custom_datatype_checkboxes", + ) + + def get_displayed_datatypes_button(self): + """Create the datatype button, centralised as used in multiple places.""" + return Button( + "Displayed Datatypes", + id="transfer_tab_displayed_datatypes_button", + ) diff --git a/tests/tests_tui/test_tui_datatypes.py b/tests/tests_tui/test_tui_datatypes.py index eafd33a89..a0fff512c 100644 --- a/tests/tests_tui/test_tui_datatypes.py +++ b/tests/tests_tui/test_tui_datatypes.py @@ -24,6 +24,8 @@ async def test_select_displayed_datatypes_create( pilot, project_name ) + self.fill_input(pilot, "#create_folders_subject_input", "sub-001") + # Open the datatypes screen await self.scroll_to_click_pause( pilot, @@ -124,6 +126,14 @@ async def test_select_displayed_datatypes_create( is False ) + # Check that input information is not deleted on refresh + assert ( + pilot.app.screen.query_one( + "#create_folders_subject_input" + ).value + == "sub-001" + ) + # Confirm also that narrow datatypes are not shown. with pytest.raises(BaseException): pilot.app.screen.query_one( @@ -148,6 +158,7 @@ async def test_select_displayed_datatypes_transfer( await self.scroll_to_click_pause( pilot, "#transfer_custom_radiobutton" ) + await self.fill_input(pilot, "#transfer_subject_input", "sub-001") await self.scroll_to_click_pause( pilot, "#transfer_tab_displayed_datatypes_button", @@ -159,6 +170,7 @@ async def test_select_displayed_datatypes_transfer( pilot.app.screen.query_one( "#displayed_datatypes_selection_list" ).toggle_all() + await self.scroll_to_click_pause( pilot, "#displayed_datatypes_save_button" ) @@ -174,6 +186,12 @@ async def test_select_displayed_datatypes_transfer( is False ) + # Check that input information is not deleted on refresh + assert ( + pilot.app.screen.query_one("#transfer_subject_input").value + == "sub-001" + ) + # Turn on a single checkbox and run a transfer, checking that # the underlying function is called correctly (monkeypatch) await self.scroll_to_click_pause(