feat #14: Implement design guide #15
2 changed files with 23 additions and 29 deletions
|
|
@ -45,7 +45,17 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
)
|
)
|
||||||
yield RadioButton("DNS Name Entry", id="dns-entry-radio")
|
yield RadioButton("DNS Name Entry", id="dns-entry-radio")
|
||||||
|
|
||||||
# IP Address Section
|
# Hostnames Section
|
||||||
|
with Vertical(classes="default-section") as hostnames:
|
||||||
|
hostnames.border_title = "Hostnames"
|
||||||
|
yield Input(
|
||||||
|
placeholder="e.g., example.com, www.example.com",
|
||||||
|
id="hostnames-input",
|
||||||
|
classes="default-input",
|
||||||
|
)
|
||||||
|
yield Static("", id="hostnames-error", classes="validation-error")
|
||||||
|
|
||||||
|
# Address sections follow Hostnames in both visual and Tab order.
|
||||||
with Vertical(classes="default-section", id="ip-section") as ip_address:
|
with Vertical(classes="default-section", id="ip-section") as ip_address:
|
||||||
ip_address.border_title = "IP Address"
|
ip_address.border_title = "IP Address"
|
||||||
yield Input(
|
yield Input(
|
||||||
|
|
@ -55,7 +65,6 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
)
|
)
|
||||||
yield Static("", id="ip-error", classes="validation-error")
|
yield Static("", id="ip-error", classes="validation-error")
|
||||||
|
|
||||||
# DNS Name Section (initially hidden)
|
|
||||||
with Vertical(
|
with Vertical(
|
||||||
classes="default-section hidden", id="dns-section"
|
classes="default-section hidden", id="dns-section"
|
||||||
) as dns_name:
|
) as dns_name:
|
||||||
|
|
@ -67,16 +76,6 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
)
|
)
|
||||||
yield Static("", id="dns-error", classes="validation-error")
|
yield Static("", id="dns-error", classes="validation-error")
|
||||||
|
|
||||||
# Hostnames Section
|
|
||||||
with Vertical(classes="default-section") as hostnames:
|
|
||||||
hostnames.border_title = "Hostnames"
|
|
||||||
yield Input(
|
|
||||||
placeholder="e.g., example.com, www.example.com",
|
|
||||||
id="hostnames-input",
|
|
||||||
classes="default-input",
|
|
||||||
)
|
|
||||||
yield Static("", id="hostnames-error", classes="validation-error")
|
|
||||||
|
|
||||||
# Comment Section
|
# Comment Section
|
||||||
with Vertical(classes="default-section") as comment:
|
with Vertical(classes="default-section") as comment:
|
||||||
comment.border_title = "Comment (optional)"
|
comment.border_title = "Comment (optional)"
|
||||||
|
|
@ -112,9 +111,8 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
"""Focus IP address input when modal opens."""
|
"""Focus the entry type choice when the modal opens."""
|
||||||
ip_input = self.query_one("#entry-type-radio", RadioSet)
|
self.query_one("#entry-type-radio", RadioSet).focus()
|
||||||
ip_input.focus()
|
|
||||||
|
|
||||||
def on_radio_set_changed(self, event: RadioSet.Changed) -> None:
|
def on_radio_set_changed(self, event: RadioSet.Changed) -> None:
|
||||||
"""Handle entry type radio button changes."""
|
"""Handle entry type radio button changes."""
|
||||||
|
|
@ -135,9 +133,7 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
if isinstance(active_section, Vertical):
|
if isinstance(active_section, Vertical):
|
||||||
active_section.border_title = "Activate Entry"
|
active_section.border_title = "Activate Entry"
|
||||||
|
|
||||||
# Focus IP input
|
self.query_one("#hostnames-input", Input).focus()
|
||||||
ip_input = self.query_one("#ip-address-input", Input)
|
|
||||||
ip_input.focus()
|
|
||||||
elif pressed_radio and pressed_radio.id == "dns-entry-radio":
|
elif pressed_radio and pressed_radio.id == "dns-entry-radio":
|
||||||
# Show DNS section, hide IP section
|
# Show DNS section, hide IP section
|
||||||
ip_section = self.query_one("#ip-section")
|
ip_section = self.query_one("#ip-section")
|
||||||
|
|
@ -155,9 +151,7 @@ class AddEntryModal(ModalScreen[HostEntry | None]):
|
||||||
"Activate Entry (DNS entries activate after resolution)"
|
"Activate Entry (DNS entries activate after resolution)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Focus DNS input
|
self.query_one("#hostnames-input", Input).focus()
|
||||||
dns_input = self.query_one("#dns-name-input", Input)
|
|
||||||
dns_input.focus()
|
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
"""Handle button presses."""
|
"""Handle button presses."""
|
||||||
|
|
|
||||||
|
|
@ -193,15 +193,15 @@ class TestAddEntryModalRadioButtonLogic:
|
||||||
# Mock the query_one method for sections and inputs
|
# Mock the query_one method for sections and inputs
|
||||||
mock_ip_section = Mock()
|
mock_ip_section = Mock()
|
||||||
mock_dns_section = Mock()
|
mock_dns_section = Mock()
|
||||||
mock_ip_input = Mock(spec=Input)
|
mock_hostname_input = Mock(spec=Input)
|
||||||
|
|
||||||
def mock_query_one(selector, widget_type=None):
|
def mock_query_one(selector, widget_type=None):
|
||||||
if selector == "#ip-section":
|
if selector == "#ip-section":
|
||||||
return mock_ip_section
|
return mock_ip_section
|
||||||
elif selector == "#dns-section":
|
elif selector == "#dns-section":
|
||||||
return mock_dns_section
|
return mock_dns_section
|
||||||
elif selector == "#ip-address-input":
|
elif selector == "#hostnames-input":
|
||||||
return mock_ip_input
|
return mock_hostname_input
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
self.modal.query_one = Mock(side_effect=mock_query_one)
|
self.modal.query_one = Mock(side_effect=mock_query_one)
|
||||||
|
|
@ -225,22 +225,22 @@ class TestAddEntryModalRadioButtonLogic:
|
||||||
# Verify IP section is shown and DNS section is hidden
|
# Verify IP section is shown and DNS section is hidden
|
||||||
mock_ip_section.remove_class.assert_called_with("hidden")
|
mock_ip_section.remove_class.assert_called_with("hidden")
|
||||||
mock_dns_section.add_class.assert_called_with("hidden")
|
mock_dns_section.add_class.assert_called_with("hidden")
|
||||||
mock_ip_input.focus.assert_called_once()
|
mock_hostname_input.focus.assert_called_once()
|
||||||
|
|
||||||
def test_radio_button_change_to_dns_entry(self):
|
def test_radio_button_change_to_dns_entry(self):
|
||||||
"""Test radio button change to DNS entry mode."""
|
"""Test radio button change to DNS entry mode."""
|
||||||
# Mock the query_one method for sections and inputs
|
# Mock the query_one method for sections and inputs
|
||||||
mock_ip_section = Mock()
|
mock_ip_section = Mock()
|
||||||
mock_dns_section = Mock()
|
mock_dns_section = Mock()
|
||||||
mock_dns_input = Mock(spec=Input)
|
mock_hostname_input = Mock(spec=Input)
|
||||||
|
|
||||||
def mock_query_one(selector, widget_type=None):
|
def mock_query_one(selector, widget_type=None):
|
||||||
if selector == "#ip-section":
|
if selector == "#ip-section":
|
||||||
return mock_ip_section
|
return mock_ip_section
|
||||||
elif selector == "#dns-section":
|
elif selector == "#dns-section":
|
||||||
return mock_dns_section
|
return mock_dns_section
|
||||||
elif selector == "#dns-name-input":
|
elif selector == "#hostnames-input":
|
||||||
return mock_dns_input
|
return mock_hostname_input
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
self.modal.query_one = Mock(side_effect=mock_query_one)
|
self.modal.query_one = Mock(side_effect=mock_query_one)
|
||||||
|
|
@ -264,7 +264,7 @@ class TestAddEntryModalRadioButtonLogic:
|
||||||
# Verify DNS section is shown and IP section is hidden
|
# Verify DNS section is shown and IP section is hidden
|
||||||
mock_ip_section.add_class.assert_called_with("hidden")
|
mock_ip_section.add_class.assert_called_with("hidden")
|
||||||
mock_dns_section.remove_class.assert_called_with("hidden")
|
mock_dns_section.remove_class.assert_called_with("hidden")
|
||||||
mock_dns_input.focus.assert_called_once()
|
mock_hostname_input.focus.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
class TestAddEntryModalSaveLogic:
|
class TestAddEntryModalSaveLogic:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue