diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java index 9b4c8684..f3700d20 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/SelectionFieldBuilder.java @@ -75,7 +75,8 @@ public final class SelectionFieldBuilder extends FieldBuilder { this.itemsSupplier, (builder.pageService.getFormTooltipMode() == PageService.FormTooltipMode.INPUT) ? this.tooltip : null, null, - actionKey); + actionKey, + this.label); final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); ((Control) selection).setLayoutData(gridData); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/MultiCheckboxSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/MultiCheckboxSelection.java index 60356191..c04d2059 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/MultiCheckboxSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/MultiCheckboxSelection.java @@ -18,6 +18,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; import ch.ethz.seb.sebserver.gui.widget.MultiSelectionCheckbox; @@ -59,7 +60,8 @@ public class MultiCheckboxSelection extends SelectionFieldBuilder implements Inp innerGrid, () -> this.getLocalizedResources(attribute, viewContext), null, - () -> this.getLocalizedResourcesAsToolTip(attribute, viewContext)) + () -> this.getLocalizedResourcesAsToolTip(attribute, viewContext), + ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name) .getTypeInstance(); selection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/RadioSelectionFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/RadioSelectionFieldBuilder.java index 0aab8d41..740c8b4c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/RadioSelectionFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/RadioSelectionFieldBuilder.java @@ -19,6 +19,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; +import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService; import ch.ethz.seb.sebserver.gui.service.examconfig.InputField; import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder; import ch.ethz.seb.sebserver.gui.widget.RadioSelection; @@ -61,7 +62,8 @@ public class RadioSelectionFieldBuilder extends SelectionFieldBuilder implements innerGrid, () -> this.getLocalizedResources(attribute, viewContext), null, - () -> this.getLocalizedResourcesAsToolTip(attribute, viewContext)) + () -> this.getLocalizedResourcesAsToolTip(attribute, viewContext), + ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name) .getTypeInstance(); selection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java index 7b4d057e..7c24d3c3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/page/impl/DefaultPageLayout.java @@ -104,6 +104,8 @@ public class DefaultPageLayout implements TemplateComposer { skeletonLayout.horizontalSpacing = 0; pageContext.getParent().setLayout(skeletonLayout); + WidgetFactory.resetTabindex(pageContext.getParent()); + composeHeader(pageContext); composeLogoBar(pageContext); composeContent(pageContext); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java index ff8dfdd5..321bf413 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/table/TableFilter.java @@ -375,7 +375,8 @@ public class TableFilter { .selectionLocalized( ch.ethz.seb.sebserver.gui.widget.Selection.Type.SINGLE, innerComposite, - resourceSupplier); + resourceSupplier, + this.attribute.columnName); this.selector .adaptToControl() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java index d49479e6..0e787eed 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ColorSelection.java @@ -128,6 +128,11 @@ public final class ColorSelection extends Composite implements Selection { this.selection = null; } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this, label); + } + private void addColorSelection(final Event event) { final Locale locale = RWT.getLocale(); RWT.setLocale(this.i18nSupport.getUsersLanguageLocale()); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java index c464b904..8cafb7e9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelection.java @@ -46,6 +46,11 @@ public final class MultiSelection extends Composite implements Selection { setLayout(gridLayout); } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this, label); + } + @Override public Type type() { return Type.MULTI; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java index b4d25663..441e1618 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCheckbox.java @@ -8,11 +8,11 @@ package ch.ethz.seb.sebserver.gui.widget; -import ch.ethz.seb.sebserver.gbl.Constants; -import ch.ethz.seb.sebserver.gbl.util.Tuple; -import ch.ethz.seb.sebserver.gbl.util.Tuple3; -import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.gui.service.page.PageService; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import org.apache.commons.lang3.StringUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; @@ -21,10 +21,11 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Listener; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.util.Tuple; +import ch.ethz.seb.sebserver.gbl.util.Tuple3; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.service.page.PageService; public final class MultiSelectionCheckbox extends Composite implements Selection { @@ -50,6 +51,11 @@ public final class MultiSelectionCheckbox extends Composite implements Selection return Type.MULTI_CHECKBOX; } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this, label); + } + @Override public void applyNewMapping(final List> mapping) { final String selectionValue = getSelectionValue(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java index 4f1628b8..328186cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/MultiSelectionCombo.java @@ -101,6 +101,11 @@ public final class MultiSelectionCombo extends Composite implements Selection { this.dropDown.setVisible(true); } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this.dropDown, label); + } + @Override public Type type() { return Type.MULTI_COMBO; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java index db0f9204..e2ef56bb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/RadioSelection.java @@ -43,6 +43,11 @@ public final class RadioSelection extends Composite implements Selection { this.radioButtons = new LinkedHashMap<>(); } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this, label); + } + @Override public Type type() { return Type.RADIO; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Selection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Selection.java index 2b1e6c3f..f23c5bc6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/Selection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/Selection.java @@ -49,6 +49,8 @@ public interface Selection { void setToolTipText(String tooltipText); + void setAriaLabel(String label); + default void applyToolTipsForItems(final List> mapping) { throw new UnsupportedOperationException("Must be implemented for this specific Selection"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java index d9eb875e..920587dc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/SingleSelection.java @@ -109,4 +109,9 @@ public final class SingleSelection extends Combo implements Selection { } } + @Override + public void setAriaLabel(final String label) { + WidgetFactory.setARIALabel(this, label); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java index b25568c4..d33e8d8f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/ThresholdList.java @@ -159,7 +159,8 @@ public final class ThresholdList extends Composite { final Selection selector = this.widgetFactory.selectionLocalized( Type.COLOR, this, null, null, null, - COLOR_SELECTION_TEXT_KEY); + COLOR_SELECTION_TEXT_KEY, + (String) null); final GridData selectorCell = new GridData(SWT.FILL, SWT.CENTER, true, false); selectorCell.horizontalIndent = 2; selector.adaptToControl().setLayoutData(selectorCell); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 744a00ca..f70af453 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -58,7 +58,6 @@ import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.PolyglotPageService; import ch.ethz.seb.sebserver.gui.service.page.PageService; -import ch.ethz.seb.sebserver.gui.service.page.impl.ComposerServiceImpl; import ch.ethz.seb.sebserver.gui.service.page.impl.DefaultPageLayout; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; @@ -68,11 +67,20 @@ import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; public class WidgetFactory { private static final String ADD_HTML_ATTR_ARIA_ROLE = "role"; + private static final String ADD_HTML_ATTR_ARIA_LABEL = "aria-label"; private static final String ADD_HTML_ATTR_TEST_ID = "test-id"; private static final String SUB_TITLE_TExT_SUFFIX = ".subtitle"; public enum AriaRole { - link + link, + button, + list, + listitem, + grid, + row, + rowgroup, + columnheader, + gridcell } private static final Logger log = LoggerFactory.getLogger(WidgetFactory.class); @@ -368,21 +376,21 @@ public class WidgetFactory { public Button buttonLocalized(final Composite parent, final String locTextKey) { final Button button = new Button(parent, SWT.NONE); - setAttribute(button, "role", "button"); + setARIARole(button, AriaRole.button); this.polyglotPageService.injectI18n(button, new LocTextKey(locTextKey)); return button; } public Button buttonLocalized(final Composite parent, final LocTextKey locTextKey) { final Button button = new Button(parent, SWT.NONE); - setAttribute(button, "role", "button"); + setARIARole(button, AriaRole.button); this.polyglotPageService.injectI18n(button, locTextKey); return button; } public Button buttonLocalized(final Composite parent, final CustomVariant variant, final String locTextKey) { final Button button = new Button(parent, SWT.NONE); - setAttribute(button, "role", "button"); + setARIARole(button, AriaRole.button); this.polyglotPageService.injectI18n(button, new LocTextKey(locTextKey)); button.setData(RWT.CUSTOM_VARIANT, variant.key); return button; @@ -395,7 +403,7 @@ public class WidgetFactory { final LocTextKey toolTipKey) { final Button button = new Button(parent, type); - setAttribute(button, "role", "button"); + setARIARole(button, AriaRole.button); this.polyglotPageService.injectI18n(button, locTextKey, toolTipKey); return button; } @@ -495,7 +503,7 @@ public class WidgetFactory { ? new Text(content, SWT.LEFT | SWT.MULTI) : new Text(content, SWT.LEFT | SWT.BORDER | SWT.MULTI); if (label != null) { - WidgetFactory.setAttribute(input, "aria-label", label); + WidgetFactory.setARIALabel(input, label); } return input; } @@ -522,7 +530,7 @@ public class WidgetFactory { : SWT.LEFT | SWT.BORDER); if (label != null) { - WidgetFactory.setAttribute(input, "aria-label", label); + WidgetFactory.setARIALabel(input, label); } return input; } @@ -539,7 +547,7 @@ public class WidgetFactory { final Text numberInput = new Text(content, (readonly) ? SWT.LEFT | SWT.READ_ONLY : SWT.RIGHT | SWT.BORDER); if (label != null) { - WidgetFactory.setAttribute(numberInput, "aria-label", this.i18nSupport.getText(label)); + setARIALabel(numberInput, this.i18nSupport.getText(label)); } if (numberCheck != null) { numberInput.addListener(SWT.Verify, event -> { @@ -643,30 +651,35 @@ public class WidgetFactory { public Tree treeLocalized(final Composite parent, final int style) { final Tree tree = new Tree(parent, style); this.polyglotPageService.injectI18n(tree); + setARIARole(tree, AriaRole.list); return tree; } public TreeItem treeItemLocalized(final Tree parent, final String locTextKey) { final TreeItem item = new TreeItem(parent, SWT.NONE); this.polyglotPageService.injectI18n(item, new LocTextKey(locTextKey)); + setARIARole(item, AriaRole.listitem); return item; } public TreeItem treeItemLocalized(final Tree parent, final LocTextKey locTextKey) { final TreeItem item = new TreeItem(parent, SWT.NONE); this.polyglotPageService.injectI18n(item, locTextKey); + setARIARole(item, AriaRole.listitem); return item; } public TreeItem treeItemLocalized(final TreeItem parent, final String locTextKey) { final TreeItem item = new TreeItem(parent, SWT.NONE); this.polyglotPageService.injectI18n(item, new LocTextKey(locTextKey)); + setARIARole(item, AriaRole.listitem); return item; } public TreeItem treeItemLocalized(final TreeItem parent, final LocTextKey locTextKey) { final TreeItem item = new TreeItem(parent, SWT.NONE); this.polyglotPageService.injectI18n(item, locTextKey); + setARIARole(item, AriaRole.listitem); return item; } @@ -749,24 +762,27 @@ public class WidgetFactory { if (listener != null) { imageButton.addListener(SWT.MouseDown, listener); } + setARIARole(imageButton, AriaRole.button); return imageButton; } public Selection selectionLocalized( final Selection.Type type, final Composite parent, - final Supplier>> itemsSupplier) { + final Supplier>> itemsSupplier, + final LocTextKey label) { - return this.selectionLocalized(type, parent, itemsSupplier, null, null); + return this.selectionLocalized(type, parent, itemsSupplier, null, null, label); } public Selection selectionLocalized( final Selection.Type type, final Composite parent, final Supplier>> itemsSupplier, - final LocTextKey toolTipTextKey) { + final String label) { - return this.selectionLocalized(type, parent, itemsSupplier, toolTipTextKey, null); + return this.selectionLocalized( + type, parent, itemsSupplier, null, null, label); } public Selection selectionLocalized( @@ -774,9 +790,9 @@ public class WidgetFactory { final Composite parent, final Supplier>> itemsSupplier, final LocTextKey toolTipTextKey, - final Supplier>> itemsToolTipSupplier) { + final LocTextKey label) { - return selectionLocalized(type, parent, itemsSupplier, toolTipTextKey, itemsToolTipSupplier, null); + return this.selectionLocalized(type, parent, itemsSupplier, toolTipTextKey, null, label); } public Selection selectionLocalized( @@ -785,7 +801,46 @@ public class WidgetFactory { final Supplier>> itemsSupplier, final LocTextKey toolTipTextKey, final Supplier>> itemsToolTipSupplier, - final String actionLocTextPrefix) { + final String label) { + + return selectionLocalized( + type, parent, itemsSupplier, toolTipTextKey, itemsToolTipSupplier, null, label); + } + + public Selection selectionLocalized( + final Selection.Type type, + final Composite parent, + final Supplier>> itemsSupplier, + final LocTextKey toolTipTextKey, + final Supplier>> itemsToolTipSupplier, + final LocTextKey label) { + + return selectionLocalized( + type, parent, itemsSupplier, toolTipTextKey, itemsToolTipSupplier, null, + this.i18nSupport.getText(label)); + } + + public Selection selectionLocalized( + final Selection.Type type, + final Composite parent, + final Supplier>> itemsSupplier, + final LocTextKey toolTipTextKey, + final Supplier>> itemsToolTipSupplier, + final String actionLocTextPrefix, + final LocTextKey label) { + return selectionLocalized( + type, parent, itemsSupplier, toolTipTextKey, itemsToolTipSupplier, actionLocTextPrefix, + this.i18nSupport.getText(label)); + } + + public Selection selectionLocalized( + final Selection.Type type, + final Composite parent, + final Supplier>> itemsSupplier, + final LocTextKey toolTipTextKey, + final Supplier>> itemsToolTipSupplier, + final String actionLocTextPrefix, + final String label) { final Selection selection; switch (type) { @@ -837,6 +892,10 @@ public class WidgetFactory { updateFunction.accept(selection); } + if (label != null) { + selection.setAriaLabel(label); + } + return selection; } @@ -940,19 +999,28 @@ public class WidgetFactory { setAttribute(widget, ADD_HTML_ATTR_ARIA_ROLE, role.name()); } + public static void setARIALabel(final Widget widget, final String label) { + setAttribute(widget, ADD_HTML_ATTR_ARIA_LABEL, label); + } + public static void setAttribute(final Widget widget, final String name, final String value) { if (!widget.isDisposed()) { final String $el = widget instanceof Text ? "$input" : "$el"; final String id = WidgetUtil.getId(widget); exec("rap.getObject( '", id, "' ).", $el, ".attr( '", name, "', '", value, "' );"); + resetTabindex(widget); + } + } - // apply tabindex reset script - final JavaScriptExecutor executor = RWT.getClient().getService(JavaScriptExecutor.class); - executor.execute(ComposerServiceImpl.TABINDEX_RESET_SCRIPT); -// exec("rap.getObject( '", id, "' ).", $el, ".attr( 'tabindex', '0' );"); -// if (widget instanceof Text) { -// exec("rap.getObject( '", id, "' ).$el.attr( 'tabindex', '0' );"); -// } + public static void resetTabindex(final Widget widget) { + if (!widget.isDisposed()) { + final String $el = widget instanceof Text ? "$input" : "$el"; + final String id = WidgetUtil.getId(widget); + + exec("rap.getObject( '", id, "' ).", $el, ".attr( 'tabindex', '0' );"); + if (widget instanceof Text) { + exec("rap.getObject( '", id, "' ).$el.attr( 'tabindex', '0' );"); + } } }