From f07d070f7f352005df3bd397e9dd30b08978b35a Mon Sep 17 00:00:00 2001 From: TatuJLund Date: Sat, 23 Nov 2024 22:22:39 +0200 Subject: [PATCH] refactor: Further improvements BookGrid A11y --- .../admin/CategoryManagementView.java | 4 +- .../tatu/vaadincreate/crud/BookGrid.java | 4 ++ .../tatu/vaadincreate/crud/BooksView.java | 1 - .../tatu/vaadincreate/crud/form/BookForm.java | 25 +++++++---- .../vaadin/tatu/vaadincreate/i18n/I18n.java | 3 +- .../tatu/vaadincreate/stats/StatsView.java | 42 +++++++++++-------- .../main/resources/translate_de_DE.properties | 3 +- .../main/resources/translate_en_GB.properties | 3 +- .../main/resources/translate_fi_FI.properties | 3 +- .../themes/vaadincreate/mixins/books.scss | 7 +++- .../themes/vaadincreate/mixins/dashboard.scss | 1 + .../themes/vaadincreate/vaadincreate.scss | 26 ++++++++++++ 12 files changed, 87 insertions(+), 35 deletions(-) diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/admin/CategoryManagementView.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/admin/CategoryManagementView.java index c227b3f..064a7f9 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/admin/CategoryManagementView.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/admin/CategoryManagementView.java @@ -173,8 +173,8 @@ class CategoryForm extends Composite { deleteButton = new Button(VaadinIcons.TRASH, e -> handleConfirmDelete()); deleteButton.addStyleName(ValoTheme.BUTTON_DANGER); - deleteButton.setDescription( - getTranslation(I18n.DELETE) + ": " + category.getName()); + deleteButton.setDescription(String.format("%s: %s", + getTranslation(I18n.DELETE), category.getName())); deleteButton.setEnabled(category.getId() != null); deleteButton.setDisableOnClick(true); diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BookGrid.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BookGrid.java index ad419ce..075de63 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BookGrid.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BookGrid.java @@ -16,6 +16,7 @@ import com.vaadin.shared.Registration; import com.vaadin.shared.ui.ContentMode; import com.vaadin.ui.Grid; +import com.vaadin.ui.JavaScript; import com.vaadin.ui.Label; import com.vaadin.ui.renderers.HtmlRenderer; import com.vaadin.ui.renderers.NumberRenderer; @@ -175,6 +176,9 @@ public void attach() { .getNumberInstance(getUI().getLocale()); decimalFormat.setMaximumFractionDigits(2); decimalFormat.setMinimumFractionDigits(2); + // Improve Grid browsing experience for screen reader users + JavaScript.eval( + "setTimeout(() => { const body = document.querySelector('tbody.v-grid-body');Array.from(body.getElementsByTagName('tr')).forEach(el => el.setAttribute('aria-live', 'polite'));}, 1000);"); } /** diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BooksView.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BooksView.java index 7826ed7..cc389a4 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BooksView.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/BooksView.java @@ -18,7 +18,6 @@ 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; diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/form/BookForm.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/form/BookForm.java index a4704b7..29614ab 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/form/BookForm.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/crud/form/BookForm.java @@ -37,8 +37,8 @@ import com.vaadin.ui.Button; import com.vaadin.ui.CheckBoxGroup; import com.vaadin.ui.Composite; -import com.vaadin.ui.CssLayout; import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; @@ -120,6 +120,7 @@ public class BookForm extends Composite implements HasI18N { protected Button discardButton = new Button(getTranslation(I18n.DISCARD)); protected Button cancelButton = new Button(getTranslation(I18n.CANCEL)); protected Button deleteButton = new Button(getTranslation(I18n.DELETE)); + private Label selectionLabel = new Label(); private AccessControl accessControl = VaadinCreateUI.get() .getAccessControl(); @@ -129,7 +130,6 @@ 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. @@ -143,6 +143,9 @@ public BookForm(BooksPresenter presenter, BookGrid grid) { setCompositionRoot(sidePanel); buildForm(); + selectionLabel.setContentMode(ContentMode.HTML); + selectionLabel.setValue("
"); + binder = new BeanValidationBinder<>(Product.class); binder.forField(price) .withConverter(new EuroConverter( @@ -358,8 +361,6 @@ private void buildForm() { category.setHeight("170px"); category.setWidthFull(); - var spacer = new CssLayout(); - // Buttons saveButton.addStyleName(ValoTheme.BUTTON_PRIMARY); saveButton.setId("save-button"); @@ -372,18 +373,18 @@ private void buildForm() { formLayout.addComponents(productName, fieldWrapper, availability, category); - formLayout.addComponent(spacer); + formLayout.addComponents(selectionLabel); formLayout.addComponents(saveButton, discardButton, cancelButton, deleteButton); - formLayout.setExpandRatio(spacer, 1); + formLayout.setExpandRatio(selectionLabel, 1); // Set ARIA attributes for the form to make it accessible - attributes = AttributeExtension.of(formLayout); + var attributes = AttributeExtension.of(formLayout); + attributes.setAttribute("tabindex", "0"); attributes.setAttribute("aria-label", - getTranslation(I18n.Books.FORM_OPENED)); + getTranslation(I18n.Books.PRODUCT_FORM)); attributes.setAttribute("role", "form"); attributes.setAttribute("aria-keyshortcuts", "Escape PageDown PageUp"); - attributes.setAttribute("aria-live", "assertive"); sidePanel.setContent(formLayout); } @@ -421,6 +422,12 @@ public void editProduct(Product product) { getId()); Page.getCurrent().getJavaScript().execute(scrollScript); } + + // Set ARIA attributes for the form to make it accessible + selectionLabel.setValue(String.format( + "
", + product.getProductName(), getTranslation(I18n.Books.OPENED))); + } @Override diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/i18n/I18n.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/i18n/I18n.java index 8276b33..552bd36 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/i18n/I18n.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/i18n/I18n.java @@ -47,7 +47,8 @@ public static final class Books { public static final String NO_MATCHES = "no-matches"; public static final String LOADING = "loading"; public static final String CLEAR_TEXT = "clear-text"; - public static final String FORM_OPENED = "form-opened"; + public static final String PRODUCT_FORM = "product-form"; + public static final String OPENED = "opened"; private Books() { // private constructor to hide the implicit public one diff --git a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/stats/StatsView.java b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/stats/StatsView.java index ee9a993..3951abd 100644 --- a/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/stats/StatsView.java +++ b/vaadincreate-ui/src/main/java/org/vaadin/tatu/vaadincreate/stats/StatsView.java @@ -175,10 +175,11 @@ private void updatePriceChart(Map priceStats) { 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(",")); + var alt = String.format("%s:%s", getTranslation(I18n.Stats.PRICES), + priceSeries + .getData().stream().map(data -> String.format("%s %s", + data.getName(), data.getY())) + .collect(Collectors.joining(","))); priceChartAttributes.setAttribute("aria-label", alt); } @@ -208,16 +209,19 @@ private void updateCategoryChart(Map categoryStats) { 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(",")); + var alt1 = String.format("%s %s:%s", + getTranslation(I18n.Stats.CATEGORIES), + getTranslation(I18n.Stats.COUNT), + titles.getData().stream().map(data -> String.format("%s %s", + data.getName(), data.getY())) + .collect(Collectors.joining(","))); + var alt2 = String.format("%s %s:%s", + getTranslation(I18n.Stats.CATEGORIES), + getTranslation(I18n.IN_STOCK), + stockCounts + .getData().stream().map(data -> String.format("%s %s", + data.getName(), data.getY())) + .collect(Collectors.joining(","))); categoryChartAttributes.setAttribute("aria-label", alt1 + " " + alt2); } @@ -238,10 +242,12 @@ private void updateAvailabilityChart( 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(",")); + var alt = String.format("%s:%s", + getTranslation(I18n.Stats.AVAILABILITIES), + availabilitySeries + .getData().stream().map(data -> String.format("%s %s", + data.getName(), data.getY())) + .collect(Collectors.joining(","))); availabilityChartAttributes.setAttribute("aria-label", alt); } diff --git a/vaadincreate-ui/src/main/resources/translate_de_DE.properties b/vaadincreate-ui/src/main/resources/translate_de_DE.properties index dbf2390..6bf114a 100644 --- a/vaadincreate-ui/src/main/resources/translate_de_DE.properties +++ b/vaadincreate-ui/src/main/resources/translate_de_DE.properties @@ -93,4 +93,5 @@ no-matches = Es gibt keine Produkte, die dem Filter entsprechen loading = Daten werden geladen clear-text = löschen sie den text ready = bereit -form-opened = buchform geöffnet +product-form = buchform +opened = geöffnet diff --git a/vaadincreate-ui/src/main/resources/translate_en_GB.properties b/vaadincreate-ui/src/main/resources/translate_en_GB.properties index cf4864e..7efd2dc 100644 --- a/vaadincreate-ui/src/main/resources/translate_en_GB.properties +++ b/vaadincreate-ui/src/main/resources/translate_en_GB.properties @@ -93,5 +93,6 @@ no-matches = No products matches the filter loading = Loading data clear-text = Clear the text ready = ready -form-opened = bookform opened +product-form = bookform +opened = opened diff --git a/vaadincreate-ui/src/main/resources/translate_fi_FI.properties b/vaadincreate-ui/src/main/resources/translate_fi_FI.properties index dd416ae..e6eecea 100644 --- a/vaadincreate-ui/src/main/resources/translate_fi_FI.properties +++ b/vaadincreate-ui/src/main/resources/translate_fi_FI.properties @@ -92,4 +92,5 @@ no-matches = Yksik loading = Ladataan tietoja clear-text = Poista teksti ready = valmis -form-opened = tuotelomake avattu +product-form = tuotelomake +opened = avattu diff --git a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/books.scss b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/books.scss index c8ba14d..93d117c 100644 --- a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/books.scss +++ b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/books.scss @@ -37,6 +37,7 @@ .bookview { div#book-grid:focus-visible { outline: 2px solid $v-focus-color; + border-radius: 2px; } } @@ -91,7 +92,11 @@ } } - + + .bookform-form:focus-visible { + outline: 2px solid $v-focus-color; + } + .bookform { right: 0; @include transition(all 300ms); diff --git a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/dashboard.scss b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/dashboard.scss index 5ae8b79..00caaa9 100644 --- a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/dashboard.scss +++ b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/mixins/dashboard.scss @@ -17,6 +17,7 @@ .dashboard-chart-focusring:focus-visible { outline: 2px solid $v-focus-color; + border-radius: 2px; } } } diff --git a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/vaadincreate.scss b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/vaadincreate.scss index 9be5327..cb3c36a 100644 --- a/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/vaadincreate.scss +++ b/vaadincreate-ui/src/main/webapp/VAADIN/themes/vaadincreate/vaadincreate.scss @@ -99,6 +99,32 @@ $fake-grid-background-color: white; } } + span#logout-2:focus-visible { + color: white; + outline: none; + + .v-icon { + color: $v-focus-color; + } + } + + span#logout-2:hover { + color: white; + outline: none; + + .v-icon { + color: $v-focus-color; + } + } + + div[aria-label="Top of dialog"] { + display: none; + } + + div[aria-label="Bottom of Dialog"] { + display: none; + } + // Hide spinner buttons from number input input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button {