Skip to content

Commit

Permalink
fix: Navigation menu role
Browse files Browse the repository at this point in the history
feat: Add accesibility data to stats
feat: Add pg up/down shortcuts to form
refactor: Make side panel to have role dialog so that book form can have role form
fix: User form role
fix: Change Delete button description to include the name for better A11y
  • Loading branch information
TatuJLund committed Nov 23, 2024
1 parent a935a41 commit c3ed249
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public AppLayout(UI ui, AccessControl accessControl) {
toggleButton.addStyleName(ValoTheme.BUTTON_SMALL);
menuLayout.addComponent(toggleButton);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ class CategoryForm extends Composite {
deleteButton = new Button(VaadinIcons.TRASH,
e -> handleConfirmDelete());
deleteButton.addStyleName(ValoTheme.BUTTON_DANGER);
deleteButton.setDescription(getTranslation(I18n.DELETE));
deleteButton.setDescription(
getTranslation(I18n.DELETE) + ": " + category.getName());
deleteButton.setEnabled(category.getId() != null);
deleteButton.setDisableOnClick(true);

Expand All @@ -196,7 +197,8 @@ private void configureNameField() {
nameField = new TextField();
var nameFieldExt = AttributeExtension.of(nameField);
nameFieldExt.setAttribute("autocomplete", "off");
nameFieldExt.setAttribute("aria-label", getTranslation(I18n.Category.CATEGORY));
nameFieldExt.setAttribute("aria-label",
getTranslation(I18n.Category.CATEGORY));
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 @@ -36,6 +36,7 @@ public class UserForm extends Composite implements HasI18N {

public UserForm() {
form.addStyleName(VaadinCreateTheme.ADMINVIEW_USERFORM);
AttributeExtension.of(form).setAttribute("role", "form");
username = new TextField(getTranslation(I18n.User.USERNAME));
username.setId("user-field");
var userNameExt = AttributeExtension.of(username);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ public void rowSelected(Product product) {
}
}

public void selectProduct(Product product) {
view.handleSelectionChange(product);
}

/**
* Saves the given product draft.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.vaadin.tatu.vaadincreate.i18n.I18n;
import org.vaadin.tatu.vaadincreate.util.Utils;

import com.vaadin.data.HasValue.ValueChangeEvent;
import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.View;
Expand Down Expand Up @@ -74,27 +75,15 @@ public BooksView() {
grid = new BookGrid();
grid.asSingleSelect().addValueChangeListener(event -> {
if (event.isUserOriginated()) {
if (form.hasChanges()) {
var dialog = createDiscardChangesConfirmDialog();
dialog.open();
dialog.addConfirmedListener(e -> {
presenter.unlockBook();
form.showForm(false);
setFragmentParameter("");
});
dialog.addCancelListener(
e -> grid.select(form.getProduct()));
} else {
presenter.rowSelected(event.getValue());
}
handleSelectionChange(event.getValue());
}
});
grid.setVisible(false);

// Display fake Grid while loading data
fakeGrid = new FakeGrid();

form = new BookForm(presenter);
form = new BookForm(presenter, grid);

var barAndGridLayout = new VerticalLayout();
var gridWrapper = new CssLayout();
Expand All @@ -112,6 +101,24 @@ public BooksView() {
presenter.init();
}

public void handleSelectionChange(Product product) {
if (form.hasChanges()) {
var dialog = createDiscardChangesConfirmDialog();
dialog.open();
dialog.addConfirmedListener(e -> {
presenter.unlockBook();
form.showForm(false);
setFragmentParameter("");
});
dialog.addCancelListener(e -> grid.select(form.getProduct()));
} else {
presenter.rowSelected(product);
if (product != null) {
grid.select(product);
}
}
}

// Filter the grid data based on the filter text
private static <T> boolean filterCondition(T value, String filterText) {
assert filterText != null : "Filter text cannot be null";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -15,6 +16,7 @@
import org.vaadin.tatu.vaadincreate.backend.data.Availability;
import org.vaadin.tatu.vaadincreate.backend.data.Category;
import org.vaadin.tatu.vaadincreate.backend.data.Product;
import org.vaadin.tatu.vaadincreate.crud.BookGrid;
import org.vaadin.tatu.vaadincreate.crud.BooksPresenter;
import org.vaadin.tatu.vaadincreate.crud.EuroConverter;
import org.vaadin.tatu.vaadincreate.i18n.HasI18N;
Expand All @@ -25,6 +27,7 @@
import com.vaadin.data.Binder;
import com.vaadin.data.provider.ListDataProvider;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.event.ShortcutListener;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.Page;
import com.vaadin.server.UserError;
Expand Down Expand Up @@ -126,14 +129,16 @@ public class BookForm extends Composite implements HasI18N {
private BooksPresenter presenter;
private boolean visible;
private boolean isValid;
private AttributeExtension attributes;

/**
* Creates a new BookForm with the given presenter.
*
* @param presenter
* the presenter for the form.
* @param grid
*/
public BookForm(BooksPresenter presenter) {
public BookForm(BooksPresenter presenter, BookGrid grid) {
this.presenter = presenter;
setCompositionRoot(sidePanel);
buildForm();
Expand Down Expand Up @@ -183,6 +188,20 @@ public BookForm(BooksPresenter presenter) {
cancelButton.setClickShortcut(KeyCode.ESCAPE);

deleteButton.addClickListener(event -> handleDelete());
addShortcutListener(
new ShortcutListener("Next", KeyCode.PAGE_DOWN, null) {
@Override
public void handleAction(Object sender, Object target) {
selectNextProduct(presenter, grid);
}
});
addShortcutListener(
new ShortcutListener("Previous", KeyCode.PAGE_UP, null) {
@Override
public void handleAction(Object sender, Object target) {
selectPreviousProduct(presenter, grid);
}
});
}

private void handleSave() {
Expand Down Expand Up @@ -322,6 +341,7 @@ private void buildForm() {
productName.setId("product-name");
productName.setWidthFull();
productName.setMaxLength(100);
AttributeExtension.of(productName).setAttribute("autocomplete", "off");
CharacterCountExtension.extend(productName);

// Layout price and stockCount horizontally
Expand Down Expand Up @@ -358,10 +378,12 @@ private void buildForm() {
formLayout.setExpandRatio(spacer, 1);

// Set ARIA attributes for the form to make it accessible
var attributes = AttributeExtension.of(formLayout);
attributes = AttributeExtension.of(formLayout);
attributes.setAttribute("aria-label",
getTranslation(I18n.Books.FORM_OPENED));
attributes.setAttribute("role", "alert");
attributes.setAttribute("role", "form");
attributes.setAttribute("aria-keyshortcuts", "Escape PageDown PageUp");
attributes.setAttribute("aria-live", "assertive");

sidePanel.setContent(formLayout);
}
Expand Down Expand Up @@ -440,5 +462,33 @@ public void focus() {
productName.focus();
}

private static List<Product> getVisibleItems(BookGrid grid) {
return grid.getDataCommunicator().fetchItemsWithRange(0,
grid.getDataCommunicator().getDataProviderSize());
}

private void selectPreviousProduct(BooksPresenter presenter,
BookGrid grid) {
if (getProduct().getId() == null) {
return;
}
var items = getVisibleItems(grid);
var current = items.indexOf(getProduct());
if (current > 0) {
presenter.selectProduct(items.get(current - 1));
}
}

private void selectNextProduct(BooksPresenter presenter, BookGrid grid) {
if (getProduct().getId() == null) {
return;
}
var items = getVisibleItems(grid);
var current = items.indexOf(getProduct());
if (current < items.size() - 1) {
presenter.selectProduct(items.get(current + 1));
}
}

private static Logger logger = LoggerFactory.getLogger(BookForm.class);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.vaadin.tatu.vaadincreate.crud.form;

import org.vaadin.tatu.vaadincreate.AttributeExtension;
import org.vaadin.tatu.vaadincreate.VaadinCreateTheme;

import com.vaadin.ui.Component;
Expand All @@ -25,6 +26,7 @@ public SidePanel() {
layout.setId("book-form");
layout.addStyleNames(VaadinCreateTheme.BOOKFORM,
VaadinCreateTheme.BOOKFORM_WRAPPER);
AttributeExtension.of(layout).setAttribute("role", "dialog");

setCompositionRoot(layout);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.vaadin.tatu.vaadincreate.stats;

import java.util.Map;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vaadin.tatu.vaadincreate.AttributeExtension;
import org.vaadin.tatu.vaadincreate.VaadinCreateTheme;
import org.vaadin.tatu.vaadincreate.auth.RolesPermitted;
import org.vaadin.tatu.vaadincreate.backend.data.Availability;
Expand Down Expand Up @@ -50,6 +52,10 @@ public class StatsView extends VerticalLayout implements View, HasI18N {

private UI ui;

private AttributeExtension availabilityChartAttributes;
private AttributeExtension priceChartAttributes;
private AttributeExtension categoryChartAttributes;

public StatsView() {
addStyleNames(VaadinCreateTheme.STATSVIEW, ValoTheme.SCROLLABLE);
dashboard = new CssLayout();
Expand Down Expand Up @@ -78,6 +84,7 @@ private CssLayout createPriceChart() {
var priceChartWrapper = new CssLayout();
priceChart = new Chart(ChartType.PIE);
priceChart.setId("price-chart");
priceChartAttributes = AttributeExtension.of(priceChart);
priceChartWrapper.addStyleName(VaadinCreateTheme.DASHBOARD_CHART);
var conf = priceChart.getConfiguration();
conf.setTitle(getTranslation(I18n.Stats.PRICES));
Expand All @@ -93,6 +100,7 @@ private CssLayout createCategoryChart() {
.addStyleName(VaadinCreateTheme.DASHBOARD_CHART_WIDE);
categoryChart = new Chart(ChartType.COLUMN);
categoryChart.setId("category-chart");
categoryChartAttributes = AttributeExtension.of(categoryChart);
var conf = categoryChart.getConfiguration();
conf.setTitle(getTranslation(I18n.CATEGORIES));
conf.setLang(lang);
Expand All @@ -105,6 +113,7 @@ private CssLayout createAvailabilityChart() {
var availabilityChartWrapper = new CssLayout();
availabilityChart = new Chart(ChartType.COLUMN);
availabilityChart.setId("availability-chart");
availabilityChartAttributes = AttributeExtension.of(availabilityChart);
availabilityChartWrapper
.addStyleName(VaadinCreateTheme.DASHBOARD_CHART);
var conf = availabilityChart.getConfiguration();
Expand Down Expand Up @@ -159,6 +168,14 @@ private void updatePriceChart(Map<String, Long> priceStats) {
priceSeries.setName(getTranslation(I18n.Stats.COUNT));
var conf = priceChart.getConfiguration();
conf.setSeries(priceSeries);

priceChartAttributes.setAttribute("role", "figure");
priceChartAttributes.setAttribute("tabindex", "0");
var alt = getTranslation(I18n.Stats.PRICES) + ":"
+ priceSeries.getData().stream()
.map(data -> data.getName() + " " + data.getY())
.collect(Collectors.joining(","));
priceChartAttributes.setAttribute("aria-label", alt);
}

// Update the charts with the new data
Expand All @@ -184,6 +201,20 @@ private void updateCategoryChart(Map<String, Long[]> categoryStats) {
conf.addSeries(stockCounts);
stockAxis.setTitle(getTranslation(I18n.IN_STOCK));
categoryStats.keySet().forEach(cat -> conf.getxAxis().addCategory(cat));

categoryChartAttributes.setAttribute("role", "figure");
categoryChartAttributes.setAttribute("tabindex", "0");
var alt1 = getTranslation(I18n.Stats.CATEGORIES) + " "
+ getTranslation(I18n.Stats.COUNT) + ":"
+ titles.getData().stream()
.map(data -> data.getName() + " " + data.getY())
.collect(Collectors.joining(","));
var alt2 = getTranslation(I18n.Stats.CATEGORIES) + " "
+ getTranslation(I18n.IN_STOCK) + ":"
+ stockCounts.getData().stream()
.map(data -> data.getName() + " " + data.getY())
.collect(Collectors.joining(","));
categoryChartAttributes.setAttribute("aria-label", alt1 + " " + alt2);
}

// Update the charts with the new data
Expand All @@ -200,6 +231,14 @@ private void updateAvailabilityChart(
.map(item -> item.getName()).toArray(String[]::new);
var axis = conf.getxAxis();
axis.setCategories(categories);

availabilityChartAttributes.setAttribute("role", "figure");
availabilityChartAttributes.setAttribute("tabindex", "0");
var alt = getTranslation(I18n.Stats.AVAILABILITIES) + ":"
+ availabilitySeries.getData().stream()
.map(data -> data.getName() + " " + data.getY())
.collect(Collectors.joining(","));
availabilityChartAttributes.setAttribute("aria-label", alt);
}

private DataSeries categorySeries(Map<String, Long[]> categories,
Expand Down

0 comments on commit c3ed249

Please sign in to comment.