Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion gem/lib/ruby_ui/avatar/avatar_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ def view_template

def default_attrs
{
loading: "lazy",
# NB: do not set loading: "lazy" here. avatar_controller hides a not-yet-loaded
# image with `display:none` (the `hidden` class) so the fallback shows. The
# browser never fetches a `loading="lazy"` image that generates no box, so its
# `load` event never fires and the image stays hidden forever (#415). shadcn/radix
# do not lazy-load the avatar image either.
data: {
ruby_ui__avatar_target: "image",
action: "load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback"
Expand Down
14 changes: 14 additions & 0 deletions gem/test/ruby_ui/avatar_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,18 @@ def test_render_with_all_items
assert_match(/class="aspect-square h-full w-full"/, output)
refute_match(/class="[^"]*\bhidden\b[^"]*aspect-square/, output)
end

def test_image_is_not_lazy_loaded
# Regression for #415: the controller hides a still-loading image with
# display:none, and a loading="lazy" image with no box is never fetched, so
# it would stay hidden forever. The avatar image must load eagerly.
output = phlex do
RubyUI.Avatar do
RubyUI.AvatarImage(src: "https://avatars.githubusercontent.com/u/246692?v=4", alt: "joeldrapper")
RubyUI.AvatarFallback { "JD" }
end
end

refute_match(/loading="lazy"/, output)
end
end
2 changes: 1 addition & 1 deletion mcp/data/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@
},
{
"path": "avatar_image.rb",
"content": "# frozen_string_literal: true\n\nmodule RubyUI\n class AvatarImage < Base\n def initialize(src:, alt: \"\", **attrs)\n @src = src\n @alt = alt\n super(**attrs)\n end\n\n def view_template\n img(**attrs)\n end\n\n private\n\n def default_attrs\n {\n loading: \"lazy\",\n data: {\n ruby_ui__avatar_target: \"image\",\n action: \"load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback\"\n },\n class: \"aspect-square h-full w-full\",\n alt: @alt,\n src: @src\n }\n end\n end\nend\n"
"content": "# frozen_string_literal: true\n\nmodule RubyUI\n class AvatarImage < Base\n def initialize(src:, alt: \"\", **attrs)\n @src = src\n @alt = alt\n super(**attrs)\n end\n\n def view_template\n img(**attrs)\n end\n\n private\n\n def default_attrs\n {\n # NB: do not set loading: \"lazy\" here. avatar_controller hides a not-yet-loaded\n # image with `display:none` (the `hidden` class) so the fallback shows. The\n # browser never fetches a `loading=\"lazy\"` image that generates no box, so its\n # `load` event never fires and the image stays hidden forever (#415). shadcn/radix\n # do not lazy-load the avatar image either.\n data: {\n ruby_ui__avatar_target: \"image\",\n action: \"load->ruby-ui--avatar#showImage error->ruby-ui--avatar#showFallback\"\n },\n class: \"aspect-square h-full w-full\",\n alt: @alt,\n src: @src\n }\n end\n end\nend\n"
}
],
"dependencies": {
Expand Down