Skip to content

Commit 85ff577

Browse files
10/UI/Table Data & Ordering accessibility validation 44005 44065 (#9504)
thx a lot!
1 parent 485d5e5 commit 85ff577

File tree

7 files changed

+211
-143
lines changed

7 files changed

+211
-143
lines changed

components/ILIAS/UI/src/Implementation/Component/Table/Renderer.php

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,31 @@ public function renderDataTable(Component\Table\Data $component, RendererInterfa
217217
$component->getAdditionalParameters()
218218
);
219219

220+
// these column counts and index numbers are meant for aria attributes in the html tpl
221+
$compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
222+
if ($component->hasMultiActions()) { // adds column with checkbox before first data column which is numbered 1.
223+
$compensate_col_index += 1;
224+
}
225+
$compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
226+
if ($component->hasMultiActions()) {
227+
$compensate_col_count += 1;
228+
}
229+
if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
230+
$compensate_col_count += 1;
231+
}
232+
220233
$id = $this->bindJavaScript($component);
221234
$tpl->setVariable('ID', $id);
222235
$tpl->setVariable('TITLE', $component->getTitle());
223-
$tpl->setVariable('COL_COUNT', (string) $component->getColumnCount());
236+
$tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
224237
$tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
225238

226239
$sortation_signal = null;
227240
// if the generator is empty, and thus invalid, we render an empty row.
228241
if (!$rows->valid()) {
229242
$this->renderFullWidthDataCell($component, $tpl, $this->txt('ui_table_no_records'));
230243
} else {
231-
$this->renderActionsHeader($default_renderer, $component, $tpl);
244+
$this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
232245
$this->appendTableRows($tpl, $rows, $default_renderer);
233246

234247
if ($component->hasMultiActions()) {
@@ -239,8 +252,8 @@ public function renderDataTable(Component\Table\Data $component, RendererInterfa
239252
$component->getMultiActionSignal(),
240253
$modal->getShowSignal()
241254
);
242-
$total_number_of_cols = count($component->getVisibleColumns()) + 2; // + selection column and action dropdown column
243-
$tpl->setVariable('COLUMN_COUNT', (string) $total_number_of_cols);
255+
$multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
256+
$tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
244257
$tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
245258
$tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
246259
}
@@ -255,15 +268,16 @@ public function renderDataTable(Component\Table\Data $component, RendererInterfa
255268
}
256269
}
257270

258-
$this->renderTableHeader($default_renderer, $component, $tpl, $sortation_signal);
271+
$this->renderTableHeader($default_renderer, $component, $tpl, $sortation_signal, $compensate_col_index);
259272
return $tpl->get();
260273
}
261274

262275
protected function renderTableHeader(
263276
RendererInterface $default_renderer,
264277
Component\Table\Data $component,
265278
Template $tpl,
266-
?Component\Signal $sortation_signal
279+
?Component\Signal $sortation_signal,
280+
int $compensate_col_index,
267281
): void {
268282
$order = $component->getOrder();
269283
$glyph_factory = $this->getUIFactory()->symbol()->glyph();
@@ -276,18 +290,19 @@ protected function renderTableHeader(
276290
$col_title = $col->getTitle();
277291
if ($col_id === $sort_col) {
278292
if ($sort_direction === Order::ASC) {
279-
$sortation = $this->txt('order_option_generic_ascending');
293+
$sortation = "ascending"; // aria-sort should not be translated and always be in English
280294
$sortation_glyph = $glyph_factory->sortAscending("#");
281295
$param_sort_direction = Order::DESC;
282296
}
283297
if ($sort_direction === Order::DESC) {
284-
$sortation = $this->txt('order_option_generic_descending');
298+
$sortation = "descending"; // aria-sort should not be translated and always be in English
285299
$sortation_glyph = $glyph_factory->sortDescending("#");
286300
}
287301
}
288302

289303
$tpl->setCurrentBlock('header_cell');
290-
$tpl->setVariable('COL_INDEX', (string) $col->getIndex());
304+
305+
$tpl->setVariable('COL_INDEX', (string) $col->getIndex() + $compensate_col_index);
291306

292307
if ($col->isSortable() && !is_null($sortation_signal)) {
293308
$sort_signal = clone $sortation_signal;
@@ -312,10 +327,11 @@ protected function renderTableHeader(
312327
protected function renderActionsHeader(
313328
RendererInterface $default_renderer,
314329
Component\Table\Table $component,
315-
Template $tpl
330+
Template $tpl,
331+
int $compensate_col_count,
316332
): void {
317333
if ($component->hasSingleActions()) {
318-
$tpl->setVariable('COL_INDEX_ACTION', (string) count($component->getColumns()));
334+
$tpl->setVariable('COL_INDEX_ACTION', (string) $component->getColumnCount() + $compensate_col_count);
319335
$tpl->setVariable('COL_TITLE_ACTION', $this->txt('actions'));
320336
}
321337

@@ -330,6 +346,12 @@ protected function renderActionsHeader(
330346
$tpl->setVariable('SELECTION_CONTROL_SELECT', $default_renderer->render($select_all));
331347
$tpl->setVariable('SELECTION_CONTROL_DESELECT', $default_renderer->render($select_none));
332348
}
349+
350+
if ($component instanceof Component\Table\Ordering) {
351+
if (!$component->isOrderingDisabled() && !$component->hasMultiActions()) {
352+
$tpl->touchBlock('header_rowselection_cell');
353+
}
354+
}
333355
}
334356

335357
/**
@@ -528,9 +550,6 @@ public function renderOrderingRow(Component\Table\OrderingRow $component, Render
528550
$cell_tpl = $this->getTemplate("tpl.orderingcell.html", true, true);
529551
$this->fillCells($component, $cell_tpl, $default_renderer);
530552

531-
$drag_handle = $this->getUIFactory()->symbol()->glyph()->dragHandle();
532-
$drag_handle = $default_renderer->render($drag_handle);
533-
$cell_tpl->setVariable('DRAG_HANDLE', $drag_handle);
534553

535554
if ($component->isOrderingDisabled()) {
536555
return $cell_tpl->get();
@@ -570,7 +589,6 @@ protected function fillCells(
570589
}
571590
$cell_tpl->setCurrentBlock('cell');
572591
$cell_tpl->setVariable('COL_TYPE', strtolower($column->getType()));
573-
$cell_tpl->setVariable('COL_INDEX', $column->getIndex());
574592
$cell_content = $row->getCellContent($col_id);
575593
if ($cell_content instanceof Component\Component) {
576594
$cell_content = $default_renderer->render($cell_content);
@@ -590,6 +608,14 @@ protected function fillCells(
590608
);
591609
$cell_tpl->setVariable('ACTION_CONTENT', $default_renderer->render($row_actions_dropdown));
592610
}
611+
612+
if ($row instanceof Component\Table\OrderingRow) {
613+
if (!$row->isOrderingDisabled()) {
614+
$drag_handle = $this->getUIFactory()->symbol()->glyph()->dragHandle();
615+
$drag_handle = $default_renderer->render($drag_handle);
616+
$cell_tpl->setVariable('DRAG_HANDLE', $drag_handle);
617+
}
618+
}
593619
}
594620

595621
/**
@@ -630,35 +656,50 @@ public function renderOrderingTable(Component\Table\Ordering $component, Rendere
630656
);
631657
}
632658

659+
// these column counts and index numbers are meant for aria attributes in the html tpl
660+
$compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
661+
// for column with checkbox and/or drag handle before first data column which is numbered 1.
662+
if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
663+
$compensate_col_index += 1; // checkbox & drag handle
664+
}
665+
if (!$component->isOrderingDisabled()) {
666+
$compensate_col_index += 1; // Position input
667+
}
668+
669+
$compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
670+
if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
671+
$compensate_col_count += 1; // checkbox & drag handle
672+
}
673+
if (!$component->isOrderingDisabled()) {
674+
$compensate_col_count += 1; // Position input
675+
}
676+
if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
677+
$compensate_col_count += 1;
678+
}
679+
633680
$tableid = $this->bindJavaScript($component) ?? $this->createId();
634-
$total_number_of_cols = count($component->getVisibleColumns());
635681

636682
if (!$component->isOrderingDisabled()) {
637-
$total_number_of_cols = $total_number_of_cols + 1;
638683
$submit = $this->getUIFactory()->button()->standard($this->txt('sorting_save'), "")
639684
->withOnLoadCode(static fn($id) => "document.getElementById('$id').addEventListener('click',
640685
function() {document.querySelector('#$tableid form.c-table-ordering__form').submit();return false;});");
641686

642687
$tpl->setVariable('FORM_BUTTONS', $default_renderer->render($submit));
643688
$tpl->setVariable('POS_INPUT_TITLE', $this->txt('table_posinput_col_title'));
644-
}
645689

646-
if (!$component->hasMultiActions()) {
647-
$tpl->touchBlock('header_rowselection_ordering_no_multi_actions');
648690
}
649691

650692
$tpl->setVariable('ID', $tableid);
651693
$tpl->setVariable('TARGET_URL', $component->getTargetURL() ? $component->getTargetURL()->__toString() : '#');
652694
$tpl->setVariable('TITLE', $component->getTitle());
653-
$tpl->setVariable('COL_COUNT', (string) $component->getColumnCount());
695+
$tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
654696
$tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
655697

656-
657698
$columns = $component->getVisibleColumns();
658699
foreach ($columns as $col_id => $col) {
659700
$col_title = $col->getTitle();
660701
$tpl->setCurrentBlock('header_cell');
661-
$tpl->setVariable('COL_INDEX', (string) $col->getIndex());
702+
$tpl->setVariable('COL_INDEX', (string) $col->getIndex() + $compensate_col_index);
662703
$tpl->setVariable('COL_TITLE', $col_title);
663704
$tpl->setVariable('COL_TYPE', strtolower($col->getType()));
664705
$tpl->parseCurrentBlock();
@@ -672,23 +713,26 @@ function() {document.querySelector('#$tableid form.c-table-ordering__form').subm
672713
->withOrderingDisabled($component->isOrderingDisabled());
673714
}
674715

675-
$this->renderActionsHeader($default_renderer, $component, $tpl);
716+
$this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
676717
$this->appendTableRows($tpl, $r, $default_renderer);
677718

678719
if ($component->hasMultiActions()) {
679-
$total_number_of_cols = $total_number_of_cols + 2;
680720
$multi_actions = $component->getMultiActions();
681721
$modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $tableid);
682722
$multi_actions_dropdown = $this->buildMultiActionsDropdown(
683723
$multi_actions,
684724
$component->getMultiActionSignal(),
685725
$modal->getShowSignal()
686726
);
727+
687728
$tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
688729
$tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
689730
}
731+
if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
732+
$multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
733+
$tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
734+
}
690735

691-
$tpl->setVariable('COLUMN_COUNT', (string) $total_number_of_cols);
692736
return $tpl->get();
693737
}
694738

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<!-- BEGIN rowselection_cell -->
2-
<td class="c-table-data__cell c-table-data__rowselection" role="gridcell" tabindex="-1">
2+
<td class="c-table-data__cell c-table-data__rowselection" tabindex="-1">
33
<input type="checkbox" value="{ROW_ID}" class="c-table-data__row-selector">
44
</td>
55
<!-- END rowselection_cell -->
66

77
<!-- BEGIN cell -->
8-
<td class="c-table-data__cell c-table-data__cell--{COL_TYPE} <!-- BEGIN highlighted --> c-table-data__cell--highlighted<!-- END highlighted -->" role="gridcell" aria-colindex="{COL_INDEX}" <!-- BEGIN with_col_span -->colspan="{COL_SPAN}"<!-- END with_col_span --> tabindex="-1">
8+
<td class="c-table-data__cell c-table-data__cell--{COL_TYPE} <!-- BEGIN highlighted --> c-table-data__cell--highlighted<!-- END highlighted -->" <!-- BEGIN with_col_span -->colspan="{COL_SPAN}"<!-- END with_col_span --> tabindex="-1">
99
<span class="c-table-data__cell__col-title">{CELL_COL_TITLE}: </span>{CELL_CONTENT}
1010
</td>
1111
<!-- END cell -->
1212

1313
<!-- BEGIN rowaction_cell -->
14-
<td class="c-table-data__cell c-table-data__rowaction" role="gridcell" tabindex="-1">
14+
<td class="c-table-data__cell c-table-data__rowaction" tabindex="-1">
1515
{ACTION_CONTENT}
1616
</td>
17-
<!-- END rowaction_cell -->
17+
<!-- END rowaction_cell -->

components/ILIAS/UI/src/templates/default/Table/tpl.datatable.html

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ <h2 class="ilHeader" id="{ID}_label">{TITLE}</h2>
77
<div class="viewcontrols">{VIEW_CONTROLS}</div>
88

99
<div class="c-table-data__table-wrapper">
10-
<table class="c-table-data__table" role="grid" aria-labelledby="{ID}_label" aria-colcount="{COL_COUNT}">
10+
<table class="c-table-data__table" aria-labelledby="{ID}_label" aria-colcount="{COL_COUNT}" role="grid">
1111
<thead>
12-
<tr class="c-table-data__header c-table-data__row" role="rowgroup">
13-
12+
<tr class="c-table-data__header c-table-data__row">
13+
1414
<!-- BEGIN header_rowselection_cell -->
15-
<th class="c-table-data__header c-table-data__cell c-table-data__header__rowselection" role="columnheader" tabindex="-1">
15+
<th class="c-table-data__header c-table-data__cell c-table-data__header__rowselection" tabindex="-1" aria-colindex="1">
1616
<div class="c-table-data__selection_all">{SELECTION_CONTROL_SELECT}</div>
1717
<div class="c-table-data__selection_none">{SELECTION_CONTROL_DESELECT}</div>
1818
</th>
1919
<!-- END header_rowselection_cell -->
2020

2121
<!-- BEGIN header_cell -->
22-
<th class="c-table-data__header c-table-data__cell c-table-data__cell--{COL_TYPE}" role="columnheader" tabindex="-1" aria-colindex="{COL_INDEX}"<!-- BEGIN header_cell_sortation --> aria-sort="{COL_SORTATION}"<!-- END header_cell_sortation -->>
22+
<th class="c-table-data__header c-table-data__cell c-table-data__cell--{COL_TYPE}" tabindex="-1" aria-colindex="{COL_INDEX}"<!-- BEGIN header_cell_sortation --> aria-sort="{COL_SORTATION}"<!-- END header_cell_sortation -->>
2323
<div class="c-table-data__header__resize-wrapper">
2424
<!-- BEGIN header_cell_sortglyph -->
2525
{COL_SORTATION_GLYPH}
@@ -30,20 +30,20 @@ <h2 class="ilHeader" id="{ID}_label">{TITLE}</h2>
3030
<!-- END header_cell -->
3131

3232
<!-- BEGIN header_action_cell -->
33-
<th class="c-table-data__header c-table-data__cell c-table-data__header__rowaction" role="columnheader" aria-colindex="{COL_INDEX_ACTION}">{COL_TITLE_ACTION}</th>
33+
<th class="c-table-data__header c-table-data__cell c-table-data__header__rowaction" aria-colindex="{COL_INDEX_ACTION}">{COL_TITLE_ACTION}</th>
3434
<!-- END header_action_cell -->
3535
</tr>
3636
</thead>
3737

38-
<tbody class="c-table-data__body" role="rowgroup">
38+
<tbody class="c-table-data__body" >
3939
<!-- BEGIN row -->
40-
<tr class="c-table-data__row {ALTERNATION}" role="row">
40+
<tr class="c-table-data__row {ALTERNATION}">
4141
{CELLS}
4242
</tr>
4343
<!-- END row -->
4444
<!-- BEGIN multi_action -->
4545
<tr>
46-
<td class="c-table-data__cell c-table-data__cell--multiaction" colspan="{COLUMN_COUNT}">
46+
<td class="c-table-data__cell c-table-data__cell--multiaction" colspan="{MULTI_ACTION_SPAN}">
4747
<div class="c-table-data__multiaction-triggerer">{MULTI_ACTION_TRIGGERER}</div>
4848
{MULTI_ACTION_ALL_MODAL}
4949
</td>
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
<!-- BEGIN rowselection_cell -->
2-
<td class="c-table-data__cell c-table-data__rowselection" role="gridcell" tabindex="-1">
2+
<td class="c-table-data__cell c-table-data__rowselection" tabindex="-1">
33
{DRAG_HANDLE}
4-
<input type="checkbox" value="{ROW_ID}" class="c-table-data__row-selector">
4+
<!-- BEGIN rowselection_checkbox --><input type="checkbox" value="{ROW_ID}" class="c-table-data__row-selector"><!-- END rowselection_checkbox -->
55
</td>
66
<!-- END rowselection_cell -->
77

88
<!-- BEGIN orderinput_cell -->
9-
<td class="c-table-data__cell c-table-data__positioninput" role="gridcell" tabindex="-1">
9+
<td class="c-table-data__cell c-table-data__positioninput" tabindex="-1">
1010
{ORDER_INPUT}
1111
</td>
1212
<!-- END orderinput_cell -->
1313

1414
<!-- BEGIN cell -->
15-
<td class="c-table-data__cell c-table-data__cell--{COL_TYPE} <!-- BEGIN highlighted --> c-table-data__cell--highlighted<!-- END highlighted -->" role="gridcell" aria-colindex="{COL_INDEX}" tabindex="-1">
16-
{CELL_CONTENT}
15+
<td class="c-table-data__cell c-table-data__cell--{COL_TYPE} <!-- BEGIN highlighted --> c-table-data__cell--highlighted<!-- END highlighted -->" tabindex="-1">
16+
<span class="c-table-data__cell__col-title">{CELL_COL_TITLE}: </span>{CELL_CONTENT}
1717
</td>
1818
<!-- END cell -->
1919

2020
<!-- BEGIN rowaction_cell -->
21-
<td class="c-table-data__cell c-table-data__rowaction" role="gridcell" tabindex="-1">
21+
<td class="c-table-data__cell c-table-data__rowaction" tabindex="-1">
2222
{ACTION_CONTENT}
2323
</td>
2424
<!-- END rowaction_cell -->

0 commit comments

Comments
 (0)