admin: put the nickname under the name in the detail header
The grid row from the last commit read as one attribute among phones
and birthdays; a nickname is closer to a name, and the header is who
the person is. Muted type-body-sm keeps the formal name the heading.
Assisted-by: GLM-5.3 via pi
diff --git a/lib/pro_tacts/admin/contacts_show.rb b/lib/pro_tacts/admin/contacts_show.rb
index 540a325..a6c8fe0 100644
--- a/lib/pro_tacts/admin/contacts_show.rb
+++ b/lib/pro_tacts/admin/contacts_show.rb
@@ -29,7 +29,16 @@ module ProTacts
div(class: "card") do
div(class: "card-body") do
div(class: "detail-header") do
- h1(class: "type-h2", style: "margin: 0;") { @contact.name || @contact.id }
+ div do
+ h1(class: "type-h2", style: "margin: 0;") { @contact.name || @contact.id }
+ # The nickname rides under the name, muted so the formal
+ # name stays the heading. In the header rather than the
+ # grid: the grid is what reaches the person, the header
+ # is who they are.
+ if @contact.nickname
+ div(class: "type-body-sm gl-muted", style: "margin-top: var(--gl-space-2xs);") { @contact.nickname }
+ end
+ end
# Only a picture earns this slot: an initials circle
# beside the name in type-h2 would repeat what the name
# already says. The dashboard rows keep theirs — there
@@ -50,17 +59,13 @@ module ProTacts
#: () -> bool
def has_data?
- !@contact.nickname.nil? ||
- @contact.phones.any? || @contact.emails.any? || @contact.addresses.any? ||
+ @contact.phones.any? || @contact.emails.any? || @contact.addresses.any? ||
!@birthday.nil? || !@contact.notes.nil?
end
# A missing TYPE parameter still gets a key: the fallback names
# the kind of value, so no row renders unlabeled in the grid.
def rows
- # The nickname leads the grid: closest to the name the header
- # already shows, ahead of the channels that reach the person.
- row("nickname", @contact.nickname) if @contact.nickname
@contact.phones.each { |phone| row(phone.type || "phone", phone.value) }
@contact.emails.each { |email| row(email.type || "email", email.value) }
@contact.addresses.each { |address| row(address.type || "address", address_lines(address)) }
diff --git a/test/pro_tacts/admin/test_contacts_pages.rb b/test/pro_tacts/admin/test_contacts_pages.rb
index a08e0ea..38294ce 100644
--- a/test/pro_tacts/admin/test_contacts_pages.rb
+++ b/test/pro_tacts/admin/test_contacts_pages.rb
@@ -283,20 +283,20 @@ class AdminContactsPagesTest < Minitest::Test
end
end
- # The nickname is an attribute like the others here — a labeled
- # grid row, leading the channels — not a second name in the header,
- # which stays the name and the avatar alone.
- def test_show_renders_the_nickname_as_a_row
+ # The nickname is a name-like fact, so it rides under the name in
+ # the header — muted, so the formal name stays the heading — and
+ # the grid keeps only what reaches the person.
+ def test_show_puts_the_nickname_under_the_name_in_the_header
red = ADA.sub("FN:Ada Lovelace", "FN:Sarah\r\nNICKNAME:Red").sub("UID:ada", "UID:red")
with_contacts({"red" => red}) do
get "/contacts/red"
- assert_includes last_response.body, '<dt class="type-label">nickname</dt>'
- assert_includes last_response.body, "Red"
- # The row leads the grid, ahead of the first phone.
body = last_response.body
- assert body.index('">nickname</dt>') < body.index('">mobile</dt>')
+ header = body.split("<dl").first
+ assert_includes header, "Sarah</h1>"
+ assert_includes header, ">Red<"
+ refute_includes body, '">nickname</dt>'
end
end