Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Show people as portraits in the lists and the bar

0fbeca853d0174030973a4019bd54071cafa77dd · Keenan Nemetz · 7d ago · parent aceaabbf

Show people as portraits in the lists and the bar

The account menu now carries the signed-in account's mark instead of a
generic user icon, and the issue and change tables show the person
rather than the name: the author is one portrait, and an issue's
assignees are the portraits beside it. The issue's assignee labels moved
out of the title cell, so a row is title plus people.

A table cell is the one place where the portrait is not decoration —
there is no printed name beside it — so those calls use avatarOnly: it
keeps the profile link and the aria-label, and does not hide itself from
a screen reader the way an avatar beside a name does.

The contributor list already used the shared avatar template, so it
picked up the new marks with the rest; checked in a browser rather than
assumed.

server/web/avatar_test.go +34−1

@@ -107,7 +107,7 @@ func TestAvatarRendersInTheChrome(t *testing.T) {
107 107 if !strings.Contains(body, `/static/dicebear-avatars.min.js`) {
108 108 t.Fatalf("the profile does not load the avatar bundle:\n%s", body)
109 109 }
110 if !strings.Contains(body, `aria-hidden="true" data-avatar="alice"`) {
110 if !strings.Contains(body, `data-avatar="alice" aria-hidden="true"`) {
111 111 t.Fatalf("the avatar is not hidden from assistive technology:\n%s", body)
112 112 }
113 113 }
@@ -138,3 +138,36 @@ func TestAvatarAssetsAreEmbedded(t *testing.T) {
138 138 t.Fatalf("dicebear.LICENSE does not read as MIT:\n%s", core)
139 139 }
140 140 }
141
142 // In an author or assignee cell the avatar is the whole content, so it stops
143 // being decoration: it must not hide from assistive technology, and it needs the
144 // name it stands for.
145 func TestAvatarOnlyCarriesTheName(t *testing.T) {
146 t.Parallel()
147 s := newSite(t)
148 view := s.web.avatarOnly(s.web.userRef("alice"))
149 if view.Label != "alice" || view.Href != "/u/alice" {
150 t.Fatalf("avatarOnly = %+v", view)
151 }
152 var out bytes.Buffer
153 if err := s.web.Tmpl.ExecuteTemplate(&out, "avatar", view); err != nil {
154 t.Fatal(err)
155 }
156 markup := out.String()
157 if strings.Contains(markup, `aria-hidden`) || strings.Contains(markup, `tabindex="-1"`) {
158 t.Fatalf("an image-only avatar is hidden from assistive technology:\n%s", markup)
159 }
160 if !strings.Contains(markup, `aria-label="alice"`) {
161 t.Fatalf("the image-only avatar has no accessible name:\n%s", markup)
162 }
163
164 // A name that is not an account still needs a name, but no link.
165 stranger := s.web.avatarOnly(s.web.userRef("nobody"))
166 out.Reset()
167 if err := s.web.Tmpl.ExecuteTemplate(&out, "avatar", stranger); err != nil {
168 t.Fatal(err)
169 }
170 if !strings.Contains(out.String(), `role="img" aria-label="nobody"`) {
171 t.Fatalf("an unknown name lost its label:\n%s", out.String())
172 }
173 }