Refactor entry details display: replace DataTable with disabled input widgets for improved clarity and user experience.
This commit is contained in:
parent
9a9161f28c
commit
de5acd4dad
5 changed files with 120 additions and 68 deletions
|
@ -331,7 +331,11 @@ class HostsManager:
|
|||
|
||||
# Remove the entry
|
||||
deleted_entry = hosts_file.entries.pop(index)
|
||||
canonical_hostname = deleted_entry.hostnames[0] if deleted_entry.hostnames else deleted_entry.ip_address
|
||||
canonical_hostname = (
|
||||
deleted_entry.hostnames[0]
|
||||
if deleted_entry.hostnames
|
||||
else deleted_entry.ip_address
|
||||
)
|
||||
|
||||
# Save the file immediately
|
||||
save_success, save_message = self.save_hosts_file(hosts_file)
|
||||
|
|
|
@ -91,12 +91,30 @@ class HostsManagerApp(App):
|
|||
# Right pane - entry details or edit form
|
||||
with Vertical(classes="common-pane right-pane") as right_pane:
|
||||
right_pane.border_title = "Entry Details"
|
||||
yield DataTable(
|
||||
id="entry-details-table",
|
||||
show_header=False,
|
||||
show_cursor=False,
|
||||
disabled=True,
|
||||
)
|
||||
|
||||
# Details display form (disabled inputs)
|
||||
with Vertical(id="entry-details-display"):
|
||||
yield Label("IP Address:")
|
||||
yield Input(
|
||||
placeholder="No entry selected",
|
||||
id="details-ip-input",
|
||||
disabled=True,
|
||||
)
|
||||
yield Label("Hostnames (comma-separated):")
|
||||
yield Input(
|
||||
placeholder="No entry selected",
|
||||
id="details-hostname-input",
|
||||
disabled=True,
|
||||
)
|
||||
yield Label("Comment:")
|
||||
yield Input(
|
||||
placeholder="No entry selected",
|
||||
id="details-comment-input",
|
||||
disabled=True,
|
||||
)
|
||||
yield Checkbox(
|
||||
"Active", id="details-active-checkbox", disabled=True
|
||||
)
|
||||
|
||||
# Edit form (initially hidden)
|
||||
with Vertical(id="entry-edit-form", classes="hidden"):
|
||||
|
|
|
@ -5,7 +5,7 @@ This module handles the display and updating of entry details
|
|||
and edit forms in the right pane.
|
||||
"""
|
||||
|
||||
from textual.widgets import Input, Checkbox, DataTable
|
||||
from textual.widgets import Input, Checkbox
|
||||
|
||||
|
||||
class DetailsHandler:
|
||||
|
@ -23,30 +23,41 @@ class DetailsHandler:
|
|||
self.update_details_display()
|
||||
|
||||
def update_details_display(self) -> None:
|
||||
"""Update the details display using a DataTable with labeled rows."""
|
||||
details_table = self.app.query_one("#entry-details-table", DataTable)
|
||||
"""Update the details display using disabled Input widgets."""
|
||||
details_display = self.app.query_one("#entry-details-display")
|
||||
edit_form = self.app.query_one("#entry-edit-form")
|
||||
|
||||
# Show details table, hide edit form
|
||||
details_table.remove_class("hidden")
|
||||
# Show details display, hide edit form
|
||||
details_display.remove_class("hidden")
|
||||
edit_form.add_class("hidden")
|
||||
|
||||
# Clear existing data
|
||||
details_table.clear()
|
||||
# Get the input widgets
|
||||
ip_input = self.app.query_one("#details-ip-input", Input)
|
||||
hostname_input = self.app.query_one("#details-hostname-input", Input)
|
||||
comment_input = self.app.query_one("#details-comment-input", Input)
|
||||
active_checkbox = self.app.query_one("#details-active-checkbox", Checkbox)
|
||||
|
||||
if not self.app.hosts_file.entries:
|
||||
# Show empty message in a single row
|
||||
if not details_table.columns:
|
||||
details_table.add_column("Field", key="field")
|
||||
details_table.add_row("No entries loaded")
|
||||
# Show empty message
|
||||
ip_input.value = ""
|
||||
ip_input.placeholder = "No entries loaded"
|
||||
hostname_input.value = ""
|
||||
hostname_input.placeholder = "No entries loaded"
|
||||
comment_input.value = ""
|
||||
comment_input.placeholder = "No entries loaded"
|
||||
active_checkbox.value = False
|
||||
return
|
||||
|
||||
# Get visible entries to check if we need to adjust selection
|
||||
visible_entries = self.app.table_handler.get_visible_entries()
|
||||
if not visible_entries:
|
||||
if not details_table.columns:
|
||||
details_table.add_column("Field", key="field")
|
||||
details_table.add_row("No visible entries")
|
||||
ip_input.value = ""
|
||||
ip_input.placeholder = "No visible entries"
|
||||
hostname_input.value = ""
|
||||
hostname_input.placeholder = "No visible entries"
|
||||
comment_input.value = ""
|
||||
comment_input.placeholder = "No visible entries"
|
||||
active_checkbox.value = False
|
||||
return
|
||||
|
||||
# If default entries are hidden and selected_entry_index points to a hidden entry,
|
||||
|
@ -73,36 +84,28 @@ class DetailsHandler:
|
|||
|
||||
entry = self.app.hosts_file.entries[self.app.selected_entry_index]
|
||||
|
||||
# Add columns for labeled rows (Field, Value) - only if not already present
|
||||
if not details_table.columns:
|
||||
details_table.add_column("Field", key="field")
|
||||
details_table.add_column("Value", key="value")
|
||||
# Update the input widgets with entry data
|
||||
ip_input.value = entry.ip_address
|
||||
ip_input.placeholder = ""
|
||||
hostname_input.value = ", ".join(entry.hostnames)
|
||||
hostname_input.placeholder = ""
|
||||
comment_input.value = entry.comment or ""
|
||||
comment_input.placeholder = "No comment"
|
||||
active_checkbox.value = entry.is_active
|
||||
|
||||
# Add rows in the same order as edit form
|
||||
details_table.add_row("IP Address", entry.ip_address, key="ip")
|
||||
details_table.add_row("Hostnames", ", ".join(entry.hostnames), key="hostnames")
|
||||
details_table.add_row("Comment", entry.comment or "", key="comment")
|
||||
details_table.add_row(
|
||||
"Active", "Yes" if entry.is_active else "No", key="active"
|
||||
)
|
||||
|
||||
# Add DNS name if present (not in edit form but good to show)
|
||||
if entry.dns_name:
|
||||
details_table.add_row("DNS Name", entry.dns_name, key="dns")
|
||||
|
||||
# Add notice for default system entries
|
||||
# For default entries, show warning in placeholder text
|
||||
if entry.is_default_entry():
|
||||
details_table.add_row("", "", key="spacer")
|
||||
details_table.add_row("⚠️ WARNING", "SYSTEM DEFAULT ENTRY", key="warning")
|
||||
details_table.add_row("Note", "This entry cannot be modified", key="note")
|
||||
ip_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
||||
hostname_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
||||
comment_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
||||
|
||||
def update_edit_form(self) -> None:
|
||||
"""Update the edit form with current entry values."""
|
||||
details_table = self.app.query_one("#entry-details-table", DataTable)
|
||||
details_display = self.app.query_one("#entry-details-display")
|
||||
edit_form = self.app.query_one("#entry-edit-form")
|
||||
|
||||
# Hide details table, show edit form
|
||||
details_table.add_class("hidden")
|
||||
# Hide details display, show edit form
|
||||
details_display.add_class("hidden")
|
||||
edit_form.remove_class("hidden")
|
||||
|
||||
if not self.app.hosts_file.entries or self.app.selected_entry_index >= len(
|
||||
|
|
|
@ -156,16 +156,27 @@ class TestHostsManagerApp:
|
|||
):
|
||||
app = HostsManagerApp()
|
||||
|
||||
# Mock the query_one method to return DataTable mock
|
||||
mock_details_table = Mock()
|
||||
mock_details_table.columns = [] # Mock empty columns list
|
||||
# Mock the query_one method to return disabled input widgets
|
||||
mock_details_display = Mock()
|
||||
mock_edit_form = Mock()
|
||||
mock_ip_input = Mock()
|
||||
mock_hostname_input = Mock()
|
||||
mock_comment_input = Mock()
|
||||
mock_active_checkbox = Mock()
|
||||
|
||||
def mock_query_one(selector, widget_type=None):
|
||||
if selector == "#entry-details-table":
|
||||
return mock_details_table
|
||||
if selector == "#entry-details-display":
|
||||
return mock_details_display
|
||||
elif selector == "#entry-edit-form":
|
||||
return mock_edit_form
|
||||
elif selector == "#details-ip-input":
|
||||
return mock_ip_input
|
||||
elif selector == "#details-hostname-input":
|
||||
return mock_hostname_input
|
||||
elif selector == "#details-comment-input":
|
||||
return mock_comment_input
|
||||
elif selector == "#details-active-checkbox":
|
||||
return mock_active_checkbox
|
||||
return Mock()
|
||||
|
||||
app.query_one = mock_query_one
|
||||
|
@ -182,12 +193,13 @@ class TestHostsManagerApp:
|
|||
|
||||
app.update_entry_details()
|
||||
|
||||
# Verify DataTable operations were called
|
||||
mock_details_table.remove_class.assert_called_with("hidden")
|
||||
# Verify input widgets were updated with entry data
|
||||
mock_details_display.remove_class.assert_called_with("hidden")
|
||||
mock_edit_form.add_class.assert_called_with("hidden")
|
||||
mock_details_table.clear.assert_called_once()
|
||||
mock_details_table.add_column.assert_called()
|
||||
mock_details_table.add_row.assert_called()
|
||||
assert mock_ip_input.value == "127.0.0.1"
|
||||
assert mock_hostname_input.value == "localhost, local"
|
||||
assert mock_comment_input.value == "Test comment"
|
||||
assert mock_active_checkbox.value
|
||||
|
||||
def test_update_entry_details_no_entries(self):
|
||||
"""Test updating entry details with no entries."""
|
||||
|
@ -200,16 +212,27 @@ class TestHostsManagerApp:
|
|||
):
|
||||
app = HostsManagerApp()
|
||||
|
||||
# Mock the query_one method to return DataTable mock
|
||||
mock_details_table = Mock()
|
||||
mock_details_table.columns = [] # Mock empty columns list
|
||||
# Mock the query_one method to return disabled input widgets
|
||||
mock_details_display = Mock()
|
||||
mock_edit_form = Mock()
|
||||
mock_ip_input = Mock()
|
||||
mock_hostname_input = Mock()
|
||||
mock_comment_input = Mock()
|
||||
mock_active_checkbox = Mock()
|
||||
|
||||
def mock_query_one(selector, widget_type=None):
|
||||
if selector == "#entry-details-table":
|
||||
return mock_details_table
|
||||
if selector == "#entry-details-display":
|
||||
return mock_details_display
|
||||
elif selector == "#entry-edit-form":
|
||||
return mock_edit_form
|
||||
elif selector == "#details-ip-input":
|
||||
return mock_ip_input
|
||||
elif selector == "#details-hostname-input":
|
||||
return mock_hostname_input
|
||||
elif selector == "#details-comment-input":
|
||||
return mock_comment_input
|
||||
elif selector == "#details-active-checkbox":
|
||||
return mock_active_checkbox
|
||||
return Mock()
|
||||
|
||||
app.query_one = mock_query_one
|
||||
|
@ -217,12 +240,16 @@ class TestHostsManagerApp:
|
|||
|
||||
app.update_entry_details()
|
||||
|
||||
# Verify DataTable operations were called for empty state
|
||||
mock_details_table.remove_class.assert_called_with("hidden")
|
||||
# Verify widgets show empty state placeholders
|
||||
mock_details_display.remove_class.assert_called_with("hidden")
|
||||
mock_edit_form.add_class.assert_called_with("hidden")
|
||||
mock_details_table.clear.assert_called_once()
|
||||
mock_details_table.add_column.assert_called_with("Field", key="field")
|
||||
mock_details_table.add_row.assert_called_with("No entries loaded")
|
||||
assert mock_ip_input.value == ""
|
||||
assert mock_ip_input.placeholder == "No entries loaded"
|
||||
assert mock_hostname_input.value == ""
|
||||
assert mock_hostname_input.placeholder == "No entries loaded"
|
||||
assert mock_comment_input.value == ""
|
||||
assert mock_comment_input.placeholder == "No entries loaded"
|
||||
assert not mock_active_checkbox.value
|
||||
|
||||
def test_update_status_default(self):
|
||||
"""Test status bar update with default information."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue