misc: redo steps markup for better a11y (#40934) #967

Merged
fpeters merged 1 commits from wip/40934-a11y-formdata-steps into main 2024-01-16 09:17:18 +01:00
6 changed files with 90 additions and 84 deletions

View File

@ -3083,7 +3083,7 @@ def test_backoffice_wfedit_single_page(pub):
resp = app.get(formdata.get_backoffice_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['2nd page']
assert [x.text for x in resp.pyquery('#steps .wcs-step--label-text')] == ['2nd page']
resp.form['f4'] = 'changed'
resp = resp.form.submit('submit')
formdata.refresh_from_storage()

View File

@ -363,7 +363,7 @@ def test_backoffice_submission_early_variable(pub):
app = login(get_app(pub))
resp = app.get('/backoffice/submission/form-title/')
assert resp.pyquery('#steps .current .label').text() == 'real page'
assert resp.pyquery('#steps .current .wcs-step--label-text').text() == 'real page'
def test_backoffice_parallel_submission(pub, autosave):

View File

@ -582,9 +582,7 @@ def test_form_submit_handling_role_info(pub):
def assert_current_page(resp, page_label):
for li_tag in resp.html.findAll('li'):
if 'current' in li_tag.attrs['class']:
assert li_tag.find_all('span')[-1].text == page_label
assert resp.pyquery('.wcs-step.current .wcs-step--label-text').text() == page_label
def test_form_multi_page(pub):
@ -1066,11 +1064,11 @@ def test_form_multi_page_condition_stored_values(pub):
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'toto'
resp = resp.form.submit('submit') # -> page 2
resp.form['f3'] = 'bar'
resp.form['f3'] = 'BAR'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('submit') # -> validation page
assert 'Check values then click submit.' in resp.text
assert 'bar' in resp.text
assert 'BAR' in resp.text
resp = resp.form.submit('previous') # -> page 3
resp = resp.form.submit('previous') # -> page 2
resp = resp.form.submit('previous') # -> page 1
@ -1078,7 +1076,7 @@ def test_form_multi_page_condition_stored_values(pub):
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('submit') # -> validation page
assert 'Check values then click submit.' in resp.text
assert 'bar' not in resp.text
assert 'BAR' not in resp.text
resp = resp.form.submit('submit')
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
@ -1093,7 +1091,7 @@ def test_form_multi_page_condition_stored_values(pub):
resp = get_app(pub).get('/test/')
resp.form['f1'] = 'toto'
resp = resp.form.submit('submit') # -> page 2
resp.form['f3'] = 'bar'
resp.form['f3'] = 'BAR'
resp = resp.form.submit('submit') # -> page 3
resp = resp.form.submit('previous') # -> page 2
resp = resp.form.submit('previous') # -> page 1
@ -5632,7 +5630,11 @@ def test_form_edit_single_page(pub):
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['1st page', '2nd page', '3rd page']
assert [x.text for x in resp.pyquery('#steps .wcs-step--label-text')] == [
'1st page',
'2nd page',
'3rd page',
]
editable.operation_mode = 'single'
editable.page_identifier = 'plop'
@ -5648,7 +5650,7 @@ def test_form_edit_single_page(pub):
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['2nd page']
assert [x.text for x in resp.pyquery('#steps .wcs-step--label-text')] == ['2nd page']
resp.form['f4'] = 'changed'
assert [x.text for x in resp.pyquery('.buttons button')] == ['Save Changes', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')
@ -5662,7 +5664,7 @@ def test_form_edit_single_page(pub):
resp = app.get(formdata.get_url())
resp = resp.form.submit('button_editable').follow()
assert [x.text for x in resp.pyquery('#steps .label')] == ['2nd page', '3rd page']
assert [x.text for x in resp.pyquery('#steps .wcs-step--label-text')] == ['2nd page', '3rd page']
resp.form['f4'] = 'other change'
assert [x.text for x in resp.pyquery('.buttons button')] == ['Next', 'Previous', 'Cancel']
assert resp.pyquery('.buttons button.form-previous[hidden][disabled]')

View File

@ -134,14 +134,14 @@ def test_i18n_form(pub, user, emails, http_requests):
app = login(get_app(pub), username='foo', password='foo')
resp = app.get(formdef.get_url())
assert resp.pyquery('#steps li.first .label').text() == 'page field'
assert resp.pyquery('#steps li.first .wcs-step--label-text').text() == 'page field'
assert resp.pyquery('#form_label_f1').text() == 'text field *'
assert resp.pyquery('[data-field-id="1"] .hint').text() == 'an hint text'
assert resp.pyquery('select [value=""]').text() == 'a second hint text'
assert resp.pyquery('[data-field-id="3"] li:first-child span').text() == 'first'
resp = app.get(formdef.get_url(), headers={'Accept-Language': 'fr'})
assert resp.pyquery('#steps li.first .label').text() == 'champ page'
assert resp.pyquery('#steps li.first .wcs-step--label-text').text() == 'champ page'
assert resp.pyquery('#form_label_f1').text() == 'champ texte*'
assert resp.pyquery('[data-field-id="1"] .hint').text() == 'un texte daide'
assert resp.pyquery('select [value=""]').text() == 'un deuxième texte daide'

View File

@ -256,55 +256,6 @@ div.WorkflowStatusWidget {
border-left: 1px solid black;
}
/** steps **/
#steps {
height: 52px;
margin-bottom: 1em;
color: #aaa;
white-space: nowrap;
overflow: hidden;
}
#steps ol {
background: #f0f0f0;
list-style: none;
padding: 0 20px;
}
#steps li {
display: inline-block;
padding-right: 1em;
list-style: none;
}
#steps span.marker {
font-size: 26px;
padding: 2px 9px;
font-weight: bold;
color: white;
text-align: center;
background: #ddd;
border: 1px solid #ddd;
-moz-border-radius: 0.7ex;
}
#steps li.current span.marker {
background: #ffa500;
border: 1px solid #ffc400;
}
#steps span.label {
font-size: 90%;
}
#steps li.current span.label {
color: black;
}
#steps li.step-before .label {
display: none;
}
/** logs **/
form#other-log-select {
margin-top: 2em;
@ -2300,24 +2251,54 @@ div#side { // w.c.s. steps in backoffice submission
padding: 0.5rem;
border-radius: 0 0 3px 3px;
}
h2 {
display: none;
}
#steps {
margin-bottom: 0;
overflow: hidden;
}
#steps.steps-1 {
// hide steps when there's a single one
display: none;
}
#steps ol {
list-style: none;
background: transparent;
margin: 0.5em;
padding: 0;
span.label {
margin-left: 0.5rem;
white-space: nowrap;
li {
display: inline-block;
}
.current span.marker {
.label {
color: #aaa;
margin-right: 1em;
}
.marker {
text-decoration: none;
display: inline-block;
font-size: 22px;
padding: 2px 9px;
font-weight: bold;
color: white;
text-align: center;
background: #ddd;
fpeters marked this conversation as resolved
Review

Finalement, une demande d'amélioration : puisqu'on est est sur une prolématique a11y, le gris #ddd utilisé pour les steps non actives n'a pas un contraste suffisant.
Pour un contraste à minima qui passe les règles WCAG, il faut un gris #595959 pour le label et un #949494 pour le chiffre.

Finalement, une demande d'amélioration : puisqu'on est est sur une prolématique a11y, le gris #ddd utilisé pour les steps non actives n'a pas un contraste suffisant. Pour un contraste à minima qui passe les règles WCAG, il faut un gris #595959 pour le label et un #949494 pour le chiffre.
border: 1px solid #ddd;
margin-right: 0.5rem;
}
.step-before .label {
display: none;
}
.current .label {
color: black;
}
.current .marker {
background: $primary-color;
border-color: darken($primary-color, 20%);
}
.wcs-step--marker-total {
display: none;
}
}
}

View File

@ -1,18 +1,41 @@
{% load i18n %}
<div id="steps" class="steps-{{page_labels|length}}">
<h3 class="sr-only">{% trans "Steps" %}</h3>
<ol>
{% for page_label in page_labels %}
{% spaceless %}
<li class="{% if forloop.first %}first{% endif %}
{% if forloop.last %}last{% endif %}
{% if forloop.counter == current_page_index %}current{% endif %}
{% if forloop.counter < current_page_index %}step-before{% endif %}
{% if forloop.counter > current_page_index %}step-after{% endif %}" >
<span class="marker">{{ forloop.counter }}</span>
<span class="label">{{ page_label }}</span>
</li>
{% endspaceless %}
{% endfor %}
</ol>
</div>
<header id="steps" class="wcs-steps steps-{{page_labels|length}}">
<h2 id="steps--title">{% trans "Steps" %}</h2>
<div role="progressbar"
aria-labelledby="steps--title"
aria-valuemin="1"
aria-valuenow="{{ current_page_no }}"
aria-valuemax="{{ page_labels|length }}"
aria-valuetext="{% blocktrans with page_no=current_page_no page_label=page_labels|get:current_page_index %}Step {{ page_no }}: {{ page_label }}{% endblocktrans %}">
{% block steps-list %}
<ol class="wcs-steps--list">
{% for page_label in page_labels %}
{% spaceless %}
<li
class="wcs-step
{% if forloop.first %}first{% endif %}
{% if forloop.last %}last{% endif %}
{% if forloop.counter == current_page_no %}current{% endif %}
{% if forloop.counter < current_page_no %}step-before{% endif %}
{% if forloop.counter > current_page_no %}step-after{% endif %}" >
<abbr
aria-label="{% blocktrans with page_no=forloop.counter page_count=page_labels|length %}Step {{ page_no }} of {{ page_count }}:{% endblocktrans %}"
title="{% blocktrans with page_no=forloop.counter page_count=page_labels|length %}Step {{ page_no }} of {{ page_count }}{% endblocktrans %}"
class="marker wcs-step--marker">
<span class="wcs-step--marker-nb">
{{ forloop.counter }}
</span>
<span class="wcs-step--marker-total">
{{ page_labels|length }}
</span>
</abbr>
<span class="label wcs-step--label"><span class="wcs-step--label-text">{{ page_label }}</span>
{% if forloop.counter == current_page_no %}<span class="sr-only"> ({% trans "current step" %})</span>{% endif %}
</span>
</li>
{% endspaceless %}
{% endfor %}
</ol>
{% endblock %}
</div>
</header>