Adopt lazy image loading throughout admin

This improves page responsiveness on first load, especially on pages with many images (eg images list in Wagtail admin)

* Lazy load thumbnails on modeladmin lists
* Update oembed photos to use lazy loading
* Use lazy loading for `MediaBlock`, the avatar in the sidebar and comments
* Just decode chooser images async
pull/9061/head
Jake Howard 2022-05-26 15:31:12 +01:00 zatwierdzone przez LB (Ben Johnston)
rodzic 5108b5f82a
commit efe8f17280
17 zmienionych plików z 45 dodań i 13 usunięć

Wyświetl plik

@ -7,6 +7,7 @@ Changelog
* Add basic keyboard control and screen reader support for page listing re-ordering (Paarth Agarwal, Thomas van der Hoeven)
* Add `PageQuerySet.private` method as an alias of `not_public` (Mehrdad Moradizadeh)
* Most images in the admin will now only load once they are visible on screen (Jake Howard)
* Allow setting default attributes on image tags (Jake Howard)
* Fix: Prevent `PageQuerySet.not_public` from returning all pages when no page restrictions exist (Mehrdad Moradizadeh)

Wyświetl plik

@ -151,7 +151,13 @@ export const CommentHeader: FunctionComponent<CommentHeaderProps> = ({
)}
</div>
{author && author.avatarUrl && (
<img className="comment-header__avatar" src={author.avatarUrl} alt="" />
<img
className="comment-header__avatar"
src={author.avatarUrl}
alt=""
decoding="async"
loading="lazy"
/>
)}
<span id={descriptionId}>
<p className="comment-header__author">{author ? author.name : ''}</p>

Wyświetl plik

@ -117,7 +117,14 @@ class MediaBlock extends Component {
<span className="MediaBlock__icon-wrapper" aria-hidden>
<Icon icon={entityType.icon} className="MediaBlock__icon" />
</span>
<img className="MediaBlock__img" src={src} alt={alt} width="256" />
<img
className="MediaBlock__img"
src={src}
alt={alt}
width="256"
decoding="async"
loading="lazy"
/>
{src ? null : (
<span className="MediaBlock__fallback">{fallbackText}</span>

Wyświetl plik

@ -20,6 +20,8 @@ exports[`MediaBlock no data, no fallback 1`] = `
<img
alt=""
className="MediaBlock__img"
decoding="async"
loading="lazy"
src=""
width="256"
/>
@ -49,6 +51,8 @@ exports[`MediaBlock no data, with fallback 1`] = `
<img
alt=""
className="MediaBlock__img"
decoding="async"
loading="lazy"
src=""
width="256"
/>
@ -100,6 +104,8 @@ exports[`MediaBlock renders 1`] = `
<img
alt=""
className="MediaBlock__img"
decoding="async"
loading="lazy"
src="example.png"
width="256"
/>

Wyświetl plik

@ -239,7 +239,12 @@ export const Menu: React.FunctionComponent<MenuProps> = ({
type="button"
>
<div className="avatar avatar-on-dark w-flex-shrink-0 !w-w-[28px] !w-h-[28px]">
<img src={user.avatarUrl} alt="" />
<img
src={user.avatarUrl}
alt=""
decoding="async"
loading="lazy"
/>
</div>
<div className="sidebar-footer__account-toggle">
<div className="sidebar-footer__account-label w-label-3">

Wyświetl plik

@ -46,6 +46,8 @@ exports[`Menu should render with the minimum required props 1`] = `
>
<img
alt=""
decoding="async"
loading="lazy"
src="https://gravatar/profile"
/>
</div>

Wyświetl plik

@ -15,7 +15,7 @@ Wagtail 4.1 is designated a Long Term Support (LTS) release. Long Term Support r
* Add basic keyboard control and screen reader support for page listing re-ordering (Paarth Agarwal, Thomas van der Hoeven)
* Add `PageQuerySet.private` method as an alias of `not_public` (Mehrdad Moradizadeh)
* Most images in the admin will now only load once they are visible on screen (Jake Howard)
* Allow setting default attributes on image tags [](adding_default_attributes_to_images) (Jake Howard)
### Bug fixes

Wyświetl plik

@ -18,7 +18,7 @@
<p>
{% blocktrans trimmed with modified_by=workflow_state.requested_by|user_display_name %}Requested by <b>{{ modified_by }}</b>{% endblocktrans %}
<span class="avatar small"><img src="{% avatar_url workflow_state.requested_by size=25 %}" alt="" /></span>
<span class="avatar small"><img src="{% avatar_url workflow_state.requested_by size=25 %}" alt="" decoding="async" loading="lazy"/></span>
</p>
<p>

Wyświetl plik

@ -1,7 +1,7 @@
{% load wagtailadmin_tags %}
<span class="avatar small">
<img src="{% avatar_url user size=25 %}" alt="" />
<img src="{% avatar_url user size=25 %}" alt="" decoding="async" loading="lazy"/>
</span>
{% if not username %}{{ user }}{% else %}{{ username }}{% endif %}

Wyświetl plik

@ -41,6 +41,8 @@ class ThumbnailMixin:
"src": self.thumb_default,
"width": self.thumb_image_width,
"class": self.thumb_classname,
"decoding": "async",
"loading": "lazy",
}
if not image:
if self.thumb_default:

Wyświetl plik

@ -6,7 +6,7 @@
{% if latest_log_entry %}
<ul>
<li>
<span class="avatar small" data-wagtail-tooltip="{{ latest_log_entry.user_display_name }}"><img src="{% avatar_url latest_log_entry.user size=25 %}" alt="" /></span>
<span class="avatar small" data-wagtail-tooltip="{{ latest_log_entry.user_display_name }}"><img src="{% avatar_url latest_log_entry.user size=25 %}" alt="" decoding="async" loading="async" /></span>
{% trans "Last updated" %}
{% include "wagtailadmin/shared/last_updated.html" with last_updated=latest_log_entry.timestamp time_prefix="at" %}
</li>

Wyświetl plik

@ -1,3 +1,5 @@
from django.utils.html import format_html
from wagtail.embeds.exceptions import EmbedException, EmbedNotFoundException
from .base import EmbedFinder
@ -52,7 +54,7 @@ class EmbedlyFinder(EmbedFinder):
# Convert photos into HTML
if oembed["type"] == "photo":
html = '<img src="%s" alt="">' % (oembed["url"],)
html = format_html('<img src="{}" alt="">', oembed["url"])
else:
html = oembed.get("html")

Wyświetl plik

@ -66,7 +66,7 @@
data-focal-point-height="{{ image.focal_point_height|default_if_none:'' }}"
data-focal-input-label="{% trans 'Image focal point' %}"
>
<img {{ rendition.attrs }} data-original-width="{{ image.width|unlocalize }}" data-original-height="{{ image.height|unlocalize }}" class="show-transparency">
<img {{ rendition.attrs }} decoding="async" data-original-width="{{ image.width|unlocalize }}" data-original-height="{{ image.height|unlocalize }}" class="show-transparency">
<div class="current-focal-point-indicator{% if not image.has_focal_point %} hidden{% endif %}"></div>
</div>

Wyświetl plik

@ -2,5 +2,5 @@
{% block chosen_icon %}
{# Empty alt because the chosen items title is already displayed next to the image. #}
<img class="chooser__image" data-chooser-image alt="" class="show-transparency" height="{{ preview.height }}" src="{{ preview.url }}" width="{{ preview.width }}">
<img class="chooser__image" data-chooser-image alt="" class="show-transparency" decoding="async" height="{{ preview.height }}" src="{{ preview.url }}" width="{{ preview.width }}">
{% endblock chosen_icon %}

Wyświetl plik

@ -211,7 +211,8 @@ class TestMissingImage(TestCase):
response = self.client.get("/events/christmas/")
self.assertContains(
response,
'<img src="/media/not-found" width="0" height="0" alt="A missing image" class="feed-image">',
'<img src="/media/not-found" width="0" height="0" alt="A missing image" \
class="feed-image">',
html=True,
)

Wyświetl plik

@ -6,7 +6,7 @@
<div class="row last-updated">
<ul>
<li>
<span class="avatar small" data-wagtail-tooltip="{{ latest_log_entry.user_display_name }}"><img src="{% avatar_url latest_log_entry.user size=25 %}" alt="" /></span>
<span class="avatar small" data-wagtail-tooltip="{{ latest_log_entry.user_display_name }}"><img src="{% avatar_url latest_log_entry.user size=25 %}" alt="" decoding="async" loading="lazy"/></span>
{% trans "Last updated" %}
{% include "wagtailadmin/shared/last_updated.html" with last_updated=latest_log_entry.timestamp time_prefix="at" %}
</li>

Wyświetl plik

@ -36,7 +36,7 @@
{% include "wagtailadmin/bulk_actions/listing_checkbox_cell.html" with obj_type="user" obj=user aria_labelledby_prefix="user_" aria_labelledby=user.pk|unlocalize aria_labelledby_suffix="_title" %}
<td id="user_{{ user.pk|unlocalize }}_title" class="title" valign="top">
<div class="title-wrapper">
<span class="avatar small"><img src="{% avatar_url user size=25 %}" alt="" /></span>
<span class="avatar small"><img src="{% avatar_url user size=25 %}" alt="" decoding="async" loading="lazy"/></span>
<a href="{% url 'wagtailusers_users:edit' user.pk %}">{{ user|user_display_name }}</a>
</div>
<ul class="actions">