Implement redesign of the Workflow Status dialog, fixing accessibility issues. Fix #8476 (#8538)

Co-authored-by: Thibaud Colas <thibaudcolas@gmail.com>
pull/8008/head
Steve Stein 2022-06-01 09:21:11 -06:00 zatwierdzone przez GitHub
rodzic 0012c344d9
commit 910844eb30
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
10 zmienionych plików z 206 dodań i 145 usunięć

Wyświetl plik

@ -21,6 +21,7 @@ Changelog
* Add ability to select multiple items at once within bulk actions selections when holding shift on subsequent clicks (Hitansh Shah)
* Upgrade notification, shown to admins on the dashboard if Wagtail is out of date, will now link to the release notes for the closest minor branch instead of the latest patch (Tibor Leupold)
* Upgrade notification can now be configured to only show updates when there is a new LTS available via `WAGTAIL_ENABLE_UPDATE_CHECK = 'lts'` (Tibor Leupold)
* Implement redesign of the Workflow Status dialog, fixing accessibility issues (Steven Steinwand)
* Fix: Typo in `ResumeWorkflowActionFormatter` message (Stefan Hammer)
* Fix: Throw a meaningful error when saving an image to an unrecognised image format (Christian Franke)
* Fix: Remove extra padding for headers with breadcrumbs on mobile viewport (Steven Steinwand)

Wyświetl plik

@ -0,0 +1,76 @@
.workflow-timeline {
@apply w-label-3;
padding: 0;
margin-top: theme('spacing.8');
margin-bottom: theme('spacing.3');
@include media-breakpoint-up(sm) {
margin-top: theme('spacing.10');
margin-bottom: theme('spacing.10');
}
&__item {
display: flex;
flex-wrap: wrap;
align-items: center;
position: relative;
padding-bottom: theme('spacing.5');
&--rejected {
color: theme('colors.critical.200');
}
&--approved {
color: theme('colors.positive.100');
}
&--in_progress {
@apply w-label-1;
}
&--pending {
color: theme('colors.grey.400');
.icon {
color: theme('colors.grey.200');
}
}
}
&__icon {
position: relative;
flex-shrink: 0;
background: theme('colors.white.DEFAULT');
margin-inline-end: theme('spacing.[2.5]');
width: theme('spacing.5');
height: theme('spacing.5');
z-index: theme('zIndex.10');
}
&__line {
position: absolute;
margin-inline-start: -1px;
height: 100%;
top: theme('spacing.4');
inset-inline-start: theme('spacing.[2.5]');
border-inline-start: theme('borderWidth.DEFAULT') dashed
theme('colors.grey.200');
}
&__footer {
display: flex;
flex-wrap: wrap;
gap: theme('spacing.4');
}
&__footer-link {
@include transition(color 0.15s ease);
font-size: theme('fontSize.14');
color: theme('colors.teal.200');
text-decoration: none;
&:hover {
color: theme('colors.teal.600');
}
}
}

Wyświetl plik

@ -134,6 +134,7 @@ These are classes for components.
@import 'components/button-select';
@import 'components/skiplink';
@import 'components/workflow-tasks';
@import 'components/workflow-timeline';
@import 'components/switch';
@import 'components/bulk_actions';

Wyświetl plik

@ -5,7 +5,7 @@ export const dialog = (
) => {
dialogs.forEach((template) => {
const html = document.documentElement;
const templateContent = template.content.firstElementChild.cloneNode(true);
const templateContent = template.content.firstElementChild;
document.body.appendChild(templateContent);
const dialogTemplate = new A11yDialog(templateContent);

Wyświetl plik

@ -26,6 +26,7 @@ depth: 1
* Add ability to select multiple items at once within bulk actions selections when holding shift on subsequent clicks (Hitansh Shah)
* Upgrade notification, shown to admins on the dashboard if Wagtail is out of date, will now link to the release notes for the closest minor branch instead of the latest patch (Tibor Leupold)
* Upgrade notification can now be configured to only show updates when there is a new LTS available via `WAGTAIL_ENABLE_UPDATE_CHECK = 'lts'` (Tibor Leupold)
* Implement redesign of the Workflow Status dialog, fixing accessibility issues (Steven Steinwand)
### Bug fixes

Wyświetl plik

@ -625,54 +625,7 @@ footer .preview {
}
}
.workflow-timeline {
list-style: none;
padding: 0;
li {
margin-bottom: 1.25em;
position: relative;
}
li:not(:last-child)::after {
content: '';
color: $color-grey-3;
position: absolute;
top: 1.5em;
inset-inline-start: calc(0.75em - 1px);
height: 100%;
border-inline-start: 2px dotted;
}
.icon {
fill: $color-grey-3;
padding-inline-end: 0.5em;
}
.in_progress {
font-weight: 700;
.icon {
fill: $color-text-base;
}
}
.approved .icon {
fill: $color-teal;
}
.needs_changes .icon,
.rejected .icon {
fill: $color-orange;
}
.cancelled .icon {
fill: $color-red-dark;
}
}
// Media for Windows High Contrast
@media (forced-colors: $media-forced-colours) {
.object {
border-top: 1px solid GrayText;

Wyświetl plik

@ -38,7 +38,6 @@
{% if workflow_state.created_at %}
{% timesince_last_update workflow_state.created_at user_display_name=workflow_state.requested_by|user_display_name use_shorthand=True as help_text %}
{% endif %}
{% url 'wagtailadmin_pages:workflow_status' page.id as action_url %}
{% trans 'View details' as action_text %}
{# Icon #}
@ -63,8 +62,24 @@
{% endblock %}
{% block action %}
{% if workflow_state %}
{% include 'wagtailadmin/pages/side_panels/includes/side_panel_button.html' with attr='data-action-workflow-status' data_url=action_url text=action_text %}
{% if workflow_state and not hide_action %}
{% trans 'Moderators approval' as workflow_dialog_title %}
{% dialog_toggle class_name='w-bg-transparent w-text-14 w-p-0 w-text-teal-200 hover:w-text-teal-600 w-inline-flex w-justify-center w-transition' dialog_id="workflow-status-dialog" text=action_text %}
{# info subtitle #}
{% if page.get_latest_revision %}
{% workflow_status_with_date workflow_state as message_heading %}
{% if workflow_state.requested_by %}
{% blocktrans trimmed with user=workflow_state.requested_by|user_display_name asvar modified_by %}by {{ user }}{% endblocktrans %}
{% endif %}
{% dialog id="workflow-status-dialog" icon_name="list-ul" title=workflow_dialog_title message_status='info' message_heading=message_heading|add:' '|add:modified_by %}
{% include 'wagtailadmin/workflows/workflow_status.html' %}
{% enddialog %}
{% else %}
{% dialog id="workflow-status-dialog" icon_name="list-ul" title=workflow_dialog_title %}
{% include 'wagtailadmin/workflows/workflow_status.html' %}
{% enddialog %}
{% endif %}
{% else %}
{% url 'wagtailadmin_pages:history' page.id as action_url %}
{% trans 'View history' as action_text %}
@ -79,22 +94,7 @@
<div class="w-flex w-space-x-3 w-mt-3">
{% icon name='info-circle' class_name='w-w-4 w-h-4 w-text-info-100 w-shrink-0' %}
<div class="w-label-3 w-flex-1">
{% if workflow_state.status == 'needs_changes' %}
{% blocktrans trimmed with time_ago=workflow_state.current_task_state.finished_at|naturaltime %}
Changes requested {{ time_ago }}
{% endblocktrans %}
{% else %}
{% with time_ago=workflow_state.current_task_state.started_at|naturaltime workflow_task=workflow_state.current_task_state.task.name %}
{% if workflow_state.status == "in_progress" %}
{% blocktrans trimmed %}Sent to {{ workflow_task }} {{ time_ago }}{% endblocktrans %}
{% else %}
{{ workflow_state.get_status_display }} {{ workflow_task }} {{ time_ago }}
{% endif %}
{% endwith %}
{% endif %}
{% if workflow_state.requested_by %}
{% blocktrans trimmed with modified_by=workflow_state.requested_by|user_display_name %}by {{ modified_by }}{% endblocktrans %}
{% endif %}
{% workflow_status_with_date workflow_state %}
</div>
</div>
{% endif %}

Wyświetl plik

@ -1,67 +1,84 @@
{% load wagtailadmin_tags i18n %}
{% include "wagtailadmin/shared/header.html" with title=workflow_state.workflow.name icon="tasks" %}
<ol class="workflow-timeline">
<div class="nice-padding">
{% if page.get_latest_revision %}
<p>
{% blocktrans trimmed with workflow_name=workflow_state.workflow.name started_at=workflow_state.created_at %}
Submitted to <em>{{ workflow_name }}</em> at {{ started_at }}
{% endblocktrans %}
{% if workflow_state.requested_by %}
{% blocktrans trimmed with modified_by=workflow_state.requested_by|user_display_name %}by {{ modified_by }}{% endblocktrans %}
<span class="avatar small"><img src="{% avatar_url page.get_latest_revision.user size=25 %}" alt="" /></span>
{% endif %}
</p>
<li class="workflow-timeline__item workflow-timeline__item--approved">
<div class="workflow-timeline__line"></div>
{% icon name="circle-check" class_name="workflow-timeline__icon" %}
{% trans "Submitted" %}
</li>
{# Workflow tasks #}
{% trans "Not started" as not_started_text %}
{% if workflow_state %}
{% for task in workflow_state.all_tasks_with_state %}
{% with status=task.task_state.status status_display=task.task_state.get_status_display|default:not_started_text %}
<li class="workflow-timeline__item workflow-timeline__item--{{ status|default:"pending" }}">
<div class="workflow-timeline__line"></div>
{% if status == "rejected" %}
<div class="w-flex">
{% icon name="warning" class_name="workflow-timeline__icon" title=status_display %}
<div class="w-flex w-flex-col">
<strong>{{ task.name }}</strong>
{% if task.task_state.finished_by %}
{% blocktrans trimmed with requested_by=task.task_state.finished_by|user_display_name %}
Changes requested by {{ requested_by }}
{% endblocktrans %}
{% else %}
{% trans "Changes requested" %}
{% endif %}
</div>
</div>
{% else %}
{% icon name="circle-check" class_name="workflow-timeline__icon" title=status_display %}
{{ task.name }}
{% if task.task_state.finished_by %}
{% blocktrans trimmed with approved_by=task.task_state.finished_by|user_display_name %}
Approved by {{ approved_by }}
{% endblocktrans %}
{% endif %}
{% endif %}
{% if task.task_state and task.task_state.get_comment %}
<div class="w-ml-[1.875rem] w-w-full">
{% trans "with comment:" %} <em>"{{ task.task_state.get_comment }}"</em>
</div>
{% endif %}
</li>
{% endwith %}
{% endfor %}
{% endif %}
<ul class="workflow-timeline">
<li class="approved">{% icon name="success" class_name="default" %}{% trans "Submitted" %}</li>
{% trans "Not started" as not_started_text %}
{% for task in workflow_tasks %}
<li data-wagtail-tooltip="{{ task.name }}: {{ task.task_state.get_status_display|default:not_started_text }}" {% if task.task_state and task.task_state.status %}class="{{ task.task_state.status }}"{% endif %}>
{% if task.task_state.status == "rejected" %}
{% icon name="warning" class_name="default" %}<strong>{{ task.name }}</strong>
{# Published status #}
<li class="workflow-timeline__item workflow-timeline__item--pending">
{% icon name="circle-check" class_name="workflow-timeline__icon" %}{% trans "Published" %}
</li>
</ol>
{% if task.task_state.finished_by %}
{% blocktrans trimmed with requested_by=task.task_state.finished_by|user_display_name %}
Changes requested by {{ requested_by }}
{% endblocktrans %}
{% else %}
{% trans "Changes requested" %}
{% endif %}
{% else %}
{% icon name="success" class_name="default" %}{{ task.name }}
{% if task.task_state.finished_by %}
{% blocktrans trimmed with approved_by=task.task_state.finished_by|user_display_name %}
Approved by {{ approved_by }}
{% endblocktrans %}
{% endif %}
{% endif %}
{% if task.task_state and task.task_state.get_comment %}
{% trans "with comment:" %} <em>"{{ task.task_state.get_comment }}"</em>
{% endif %}
</li>
{% endfor %}
<li>{% icon name="success" class_name="default" %}{% trans "Published" %}</li>
</ul>
{# Footer Actions #}
<div class="workflow-timeline__footer">
<a href="{% url 'wagtailadmin_pages:history' page.id %}" class="button">
{% trans "See full history" %}
</a>
<p>
<a href="{% url 'wagtailadmin_pages:history' page.id %}" class="button button-small">
{% trans "See full history" %}
</a>
{% with page.get_latest_revision as latest_revision %}
{% with page.get_latest_revision as latest_revision %}
<div class="w-flex">
{% if page.live_revision %}
<a href="{% url 'wagtailadmin_pages:revisions_compare' page.id 'live' latest_revision.id %}" class="button button-small button-secondary">
<a href="{% url 'wagtailadmin_pages:revisions_compare' page.id 'live' latest_revision.id %}" class="workflow-timeline__footer-link">
{% trans "Compare with live version" %}
</a>
{% endif %}
{% with latest_revision.get_previous as previous_revision %}
{% if previous_revision %}
<a href="{% url 'wagtailadmin_pages:revisions_compare' page.id previous_revision.id latest_revision.id %}" class="button button-small button-secondary">{% trans 'Compare with previous version' %}</a>
{# Only show the separator if there are two links. #}
{% if page.live_revision %}
<div class="w-mx-2" aria-hidden="true">|</div>
{% endif %}
<a href="{% url 'wagtailadmin_pages:revisions_compare' page.id previous_revision.id latest_revision.id %}"
class="workflow-timeline__footer-link">{% trans 'Compare with previous version' %}</a>
{% endif %}
{% endwith %}
{% endwith %}
</p>
</div>
{% endwith %}
</div>

Wyświetl plik

@ -963,3 +963,21 @@ def dialog_toggle(dialog_id, class_name="", text=None):
# dialog_id must match the ID of the dialog you are toggling
"dialog_id": dialog_id,
}
@register.simple_tag()
def workflow_status_with_date(workflow_state):
translation_context = {
"finished_at": naturaltime(workflow_state.current_task_state.finished_at),
"started_at": naturaltime(workflow_state.current_task_state.started_at),
"task_name": workflow_state.current_task_state.task.name,
"status_display": workflow_state.get_status_display,
}
if workflow_state.status == "needs_changes":
return _("Changes requested %(finished_at)s") % translation_context
if workflow_state.status == "in_progress":
return _("Sent to %(task_name)s %(started_at)s") % translation_context
return _("%(status_display)s %(task_name)s %(started_at)s") % translation_context

Wyświetl plik

@ -915,10 +915,6 @@ class TestSubmitToWorkflow(TestCase, WagtailTestUtils):
self.submit()
response = self.client.get(edit_url)
workflow_status_url = reverse(
"wagtailadmin_pages:workflow_status", args=(self.page.id,)
)
self.assertContains(response, workflow_status_url)
self.assertRegex(
response.content.decode("utf-8"),
r"Sent to[\s|\n]+{}".format(self.page.current_workflow_task.name),
@ -2334,28 +2330,30 @@ class TestWorkflowStatus(TestCase, WagtailTestUtils):
)
def test_workflow_status_modal(self):
workflow_status_url = reverse(
"wagtailadmin_pages:workflow_status", args=(self.page.id,)
)
# The page workflow status view should return permission denied when the page is but a draft
response = self.client.get(workflow_status_url)
self.assertRedirects(response, "/admin/")
response = self.client.get(self.edit_url)
html = response.content.decode("utf-8")
self.assertNotIn('id="workflow-status-dialog"', html)
# Submit for moderation
self.submit()
response = self.client.get(workflow_status_url)
self.assertEqual(response.status_code, 200)
html = response.json().get("html")
self.assertIn(self.task_1.name, html)
self.assertIn("{}: In progress".format(self.task_1.name), html)
self.assertIn(self.task_2.name, html)
self.assertIn("{}: Not started".format(self.task_2.name), html)
response = self.client.get(self.edit_url)
html = response.content.decode("utf-8")
self.assertIn(
"In progress\n </span>\n {}".format(
self.task_1.name
),
html,
)
self.assertIn(
"Not started\n </span>\n {}".format(
self.task_2.name
),
html,
)
self.assertIn(reverse("wagtailadmin_pages:history", args=(self.page.id,)), html)
self.assertTemplateUsed(response, "wagtailadmin/workflows/workflow_status.html")
def test_status_through_workflow_cycle(self):
self.login(self.superuser)
response = self.client.get(self.edit_url)
@ -2415,20 +2413,16 @@ class TestWorkflowStatus(TestCase, WagtailTestUtils):
)
def test_workflow_status_modal_task_comments(self):
workflow_status_url = reverse(
"wagtailadmin_pages:workflow_status", args=(self.page.id,)
)
self.submit()
self.workflow_action("reject")
response = self.client.get(workflow_status_url)
self.assertIn("needs some changes", response.json().get("html"))
response = self.client.get(self.edit_url)
self.assertIn("needs some changes", response.content.decode("utf-8"))
self.submit()
self.workflow_action("approve")
response = self.client.get(workflow_status_url)
self.assertIn("good work", response.json().get("html"))
response = self.client.get(self.edit_url)
self.assertIn("good work", response.content.decode("utf-8"))
def test_workflow_edit_locked_message(self):
self.submit()