Skip to content

Commit

Permalink
fix: Improve main menu accessibility
Browse files Browse the repository at this point in the history
refactor: Move announcer from BookForm to AppLayout
fix: Announce view when opening
fix: Announce cross field validation error
refactor: Move AdminView styles out of AboutView mixin
refactor: Sub class menu button
  • Loading branch information
TatuJLund committed Dec 1, 2024
1 parent e27430e commit eafdf9a
Show file tree
Hide file tree
Showing 23 changed files with 181 additions and 78 deletions.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
import org.vaadin.tatu.vaadincreate.backend.data.User.Role;
import org.vaadin.tatu.vaadincreate.eventbus.EventBus;
import org.vaadin.tatu.vaadincreate.eventbus.EventBus.EventBusListener;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;
import org.vaadin.tatu.vaadincreate.util.Utils;

import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.shared.Version;
import com.vaadin.shared.ui.ContentMode;
Expand All @@ -33,7 +31,7 @@
@AllPermitted
@SuppressWarnings({ "serial", "java:S2160" })
public class AboutView extends VerticalLayout
implements View, EventBusListener, HasI18N {
implements VaadinCreateView, EventBusListener {

public static final String VIEW_NAME = "about";

Expand Down Expand Up @@ -73,9 +71,7 @@ public AboutView() {

adminsNoteField.addValueChangeListener(this::handleValueChange);
adminsNoteField.setValueChangeMode(ValueChangeMode.BLUR);
adminsNoteField.addBlurListener(e -> {
closeEditor();
});
adminsNoteField.addBlurListener(e -> closeEditor());
setSizeFull();
setMargin(false);
setStyleName(VaadinCreateTheme.ABOUT_VIEW);
Expand Down Expand Up @@ -152,6 +148,8 @@ private CustomLayout createAboutContent() {

@Override
public void enter(ViewChangeEvent event) {
openingView(VIEW_NAME);

Message message = getService().getMessage();
adminsNote.setCaption(
Utils.formatDate(message.getDateStamp(), getLocale()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class AppLayout extends Composite implements HasI18N {
private final CssLayout title;
private final UI ui;
private AccessControl accessControl;
private Label announcer;

/**
* Constructor.
Expand Down Expand Up @@ -92,7 +93,7 @@ public AppLayout(UI ui, AccessControl accessControl) {
toggleButton.addStyleName(ValoTheme.BUTTON_SMALL);
menuLayout.addComponent(toggleButton);

AttributeExtension.of(menuItems).setAttribute("role", "menu");
AttributeExtension.of(menuItems).setAttribute("role", "navigation");
menuLayout.addComponent(menuItems);
menuItems.addStyleName(ValoTheme.MENU_ITEMS);

Expand Down Expand Up @@ -122,10 +123,14 @@ public AppLayout(UI ui, AccessControl accessControl) {
nav.addViewChangeListener(new ViewChangeListener() {
@Override
public void afterViewChange(ViewChangeEvent event) {
String viewName = event.getViewName();
if (viewName.isEmpty()) {
viewName = "about";
}
clearSelected();
setSelected(event.getViewName());
setSelected(viewName);
logger.info("User '{}' navigated to view '{}'", getUserName(),
event.getViewName());
viewName);
menuLayout.removeStyleName(ValoTheme.MENU_VISIBLE);
}

Expand All @@ -136,9 +141,35 @@ public boolean beforeViewChange(ViewChangeEvent event) {
}

});

announcer = new Label();
announcer.setContentMode(ContentMode.HTML);
announcer.setPrimaryStyleName("announcer");
layout.addComponent(announcer);

setCompositionRoot(layout);
}

/**
* Announces a message using ARIA live regions to make it accessible to
* screen readers. This method sets the ARIA attributes for the form to
* ensure the announcement is conveyed to users with assistive technologies.
*
* @param announcement
* the message to be announced
*/
public void announce(String announcement) {
if (isAttached()) {
announcer.setValue("");
getUI().push();
getUI().runAfterRoundTrip(() ->
// Set ARIA attributes for the form to make it accessible
announcer.setValue(String.format(
"<div role='alert' aria-live='assertive' aria-atomic='true' aria-label='%s'></div>",
announcement)));
}
}

private void handleConfirmLogoutWhenChanges(UI ui, Navigator nav) {
// Use runAfterLeaveConfirmation wrapper to run the logout in based
// on beforeLeave of the current view. E.g. if BooksView has changes
Expand Down Expand Up @@ -191,16 +222,8 @@ public void addView(Class<? extends View> view, String viewName,
if (!hasAccessToView(view)) {
return;
}
var menuItem = new Button(viewName);
menuItem.setId(path);
menuItem.setData(path);
menuItem.addClickListener(e -> ui.getNavigator().navigateTo(path));
menuItem.setPrimaryStyleName(ValoTheme.MENU_ITEM);
if (path.equals("")) {
menuItem.addStyleName(ValoTheme.MENU_SELECTED);
}
var menuItem = new MenuButton(viewName, path, icon);
ui.getNavigator().addView(path, view);
menuItem.setIcon(icon);
menuItems.addComponent(menuItem);
}

Expand All @@ -216,11 +239,8 @@ private void clearSelected() {
private void setSelected(String path) {
var iter = menuItems.iterator();
while (iter.hasNext()) {
var menuItem = iter.next();
menuItem.removeStyleName(ValoTheme.MENU_SELECTED);
if (((Button) menuItem).getData().toString().equals(path)) {
menuItem.addStyleName(ValoTheme.MENU_SELECTED);
}
var menuItem = (MenuButton) iter.next();
menuItem.setSelected(menuItem.getPath().equals(path));
}
}

Expand All @@ -229,5 +249,43 @@ private static String getUserName() {
: "";
}

public class MenuButton extends Button {

private String path;
private String caption;
private AttributeExtension attributes;

public MenuButton(String caption, String path, Resource icon) {
super(caption);
this.path = path;
this.caption = caption;
setId(path);
setData(path);
addClickListener(e -> ui.getNavigator().navigateTo(path));
setPrimaryStyleName(ValoTheme.MENU_ITEM);
if (path.equals("")) {
addStyleName(ValoTheme.MENU_SELECTED);
}
setIcon(icon);
attributes = AttributeExtension.of(this);
attributes.setAttribute("role", "link");
}

public String getPath() {
return path;
}

public void setSelected(boolean selected) {
if (selected) {
addStyleName(ValoTheme.MENU_SELECTED);
attributes.setAttribute("aria-label",
caption + " " + getTranslation(I18n.CURRENT_PAGE));
} else {
removeStyleName(ValoTheme.MENU_SELECTED);
attributes.setAttribute("aria-label", caption);
}
}
}

private static Logger logger = LoggerFactory.getLogger(AppLayout.class);
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ public static VaadinCreateUI get() {
return (VaadinCreateUI) UI.getCurrent();
}

/**
* Announces a message using ARIA live regions to make it accessible to
* screen readers. This method sets the ARIA attributes for the form to
* ensure the announcement is conveyed to users with assistive technologies.
*
* @param announcement
* the message to be announced
*/
public void announce(String announcement) {
((AppLayout) getContent()).announce(announcement);
}

/**
* Get AccessControl in use
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.vaadin.tatu.vaadincreate;

import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;

import com.vaadin.navigator.View;
import com.vaadin.ui.UI;

public interface VaadinCreateView extends HasI18N, View {

/**
* Sets the title of the current page and announces the opening of a view.
*
* @param viewName
* the name of the view to be opened
*/
public default void openingView(String viewName) {
UI.getCurrent().getPage().setTitle(getTranslation(viewName));
VaadinCreateUI.get().announce(
getTranslation(viewName) + " " + getTranslation(I18n.OPENED));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.vaadin.tatu.vaadincreate.AttributeExtension;
import org.vaadin.tatu.vaadincreate.ConfirmDialog;
import org.vaadin.tatu.vaadincreate.backend.data.Category;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;

import com.vaadin.data.BeanValidationBinder;
Expand All @@ -27,7 +26,7 @@

@SuppressWarnings({ "serial", "java:S2160" })
public class CategoryManagementView extends VerticalLayout
implements TabView, HasI18N {
implements TabView {

public static final String VIEW_NAME = "categories";

Expand Down Expand Up @@ -81,6 +80,7 @@ private void addCategory() {

@Override
public void enter() {
openingView(VIEW_NAME);
presenter.requestUpdateCategories();
newCategoryButton.setEnabled(true);
}
Expand Down Expand Up @@ -198,7 +198,7 @@ private void configureNameField() {
var nameFieldExt = AttributeExtension.of(nameField);
nameFieldExt.setAttribute("autocomplete", "off");
nameFieldExt.setAttribute("aria-label",
getTranslation(I18n.Category.CATEGORY));
getTranslation(I18n.Category.CATEGORY_BAME));
nameFieldExt.removeAttribute("aria-labelledby");
nameField.setId(String.format("name-%s", category.getId()));
nameField.setValueChangeMode(ValueChangeMode.LAZY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ public ComponentList(ValueProvider<T, V> provider) {
createCategoryListing(provider);
panel.setSizeFull();
panel.setContent(grid);
panel.addStyleNames(VaadinCreateTheme.ADMINVIEW_CATEGORY_GRID,
ValoTheme.PANEL_BORDERLESS);
panel.addStyleName(ValoTheme.PANEL_BORDERLESS);
setCompositionRoot(panel);
}

Expand Down Expand Up @@ -97,7 +96,8 @@ private void createCategoryListing(ValueProvider<T, V> provider) {
grid.setHeaderRowHeight(1);
grid.setSizeFull();
grid.setSelectionMode(SelectionMode.NONE);
grid.addStyleNames(VaadinCreateTheme.GRID_NO_STRIPES,
grid.addStyleNames(VaadinCreateTheme.ADMINVIEW_CATEGORY_GRID,
VaadinCreateTheme.GRID_NO_STRIPES,
VaadinCreateTheme.GRID_NO_BORDERS,
VaadinCreateTheme.GRID_NO_CELL_FOCUS);
grid.setHeightMode(HeightMode.ROW);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package org.vaadin.tatu.vaadincreate.admin;

public interface TabView {
import org.vaadin.tatu.vaadincreate.VaadinCreateUI;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;

import com.vaadin.ui.UI;

public interface TabView extends HasI18N {

/**
* Returns the name of the tab.
Expand All @@ -14,4 +20,10 @@ public interface TabView {
*/
public void enter();

public default void openingView(String viewName) {
UI.getCurrent().getPage().setTitle(getTranslation(viewName));
VaadinCreateUI.get().announce(
getTranslation(viewName) + " " + getTranslation(I18n.OPENED));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.vaadin.tatu.vaadincreate.ConfirmDialog;
import org.vaadin.tatu.vaadincreate.VaadinCreateTheme;
import org.vaadin.tatu.vaadincreate.backend.data.User;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;

import com.vaadin.data.ValidationException;
Expand All @@ -22,8 +21,7 @@
import com.vaadin.ui.themes.ValoTheme;

@SuppressWarnings({ "serial", "java:S2160" })
public class UserManagementView extends VerticalLayout
implements TabView, HasI18N {
public class UserManagementView extends VerticalLayout implements TabView {

public static final String VIEW_NAME = "users";

Expand Down Expand Up @@ -181,11 +179,12 @@ public void showSaveConflict() {
Notification.Type.WARNING_MESSAGE);
form.clear();
disableButtons();
userSelect.setValue(null);
userSelect.setValue(null);
}

@Override
public void enter() {
openingView(VIEW_NAME);
presenter.requestUpdateUsers();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@
import org.vaadin.tatu.vaadincreate.ConfirmDialog;
import org.vaadin.tatu.vaadincreate.VaadinCreateTheme;
import org.vaadin.tatu.vaadincreate.VaadinCreateUI;
import org.vaadin.tatu.vaadincreate.VaadinCreateView;
import org.vaadin.tatu.vaadincreate.auth.AccessControl;
import org.vaadin.tatu.vaadincreate.auth.RolesPermitted;
import org.vaadin.tatu.vaadincreate.backend.data.Category;
import org.vaadin.tatu.vaadincreate.backend.data.Product;
import org.vaadin.tatu.vaadincreate.backend.data.User.Role;
import org.vaadin.tatu.vaadincreate.crud.form.BookForm;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
import org.vaadin.tatu.vaadincreate.i18n.I18n;
import org.vaadin.tatu.vaadincreate.util.Utils;

import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewBeforeLeaveEvent;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.ui.Alignment;
Expand All @@ -42,7 +41,7 @@
*/
@SuppressWarnings({ "serial", "java:S2160" })
@RolesPermitted({ Role.USER, Role.ADMIN })
public class BooksView extends CssLayout implements View, HasI18N {
public class BooksView extends CssLayout implements VaadinCreateView {

public static final String VIEW_NAME = "inventory";

Expand Down Expand Up @@ -194,6 +193,7 @@ public void attach() {

@Override
public void enter(ViewChangeEvent event) {
openingView(VIEW_NAME);
draft = presenter.getDraft();
params = event.getParameters();
if (!accessControl.isUserInRole(Role.ADMIN)) {
Expand Down
Loading

0 comments on commit eafdf9a

Please sign in to comment.