Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/unity-bootstrap-theme/src/js/card-bodies.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function initCardBodies() {

cardBodies.forEach((cardBody, index) => {
const paragraph = cardBody.querySelector("div p");
const originalText = paragraph.textContent;
const originalText = paragraph?.textContent;
const style = window.getComputedStyle(cardBody);

// Get the number of lines allowed (line clamp), usually set via CSS like -webkit-line-clamp
Expand All @@ -44,7 +44,7 @@ function initCardBodies() {
const maxHeight = lineClamp * actualLineHeight;

// Check if the paragraph exceeds the maximum allowed height
if (paragraph.offsetHeight >= maxHeight) {
if (paragraph?.offsetHeight >= maxHeight) {
let visibleText = "";
const words = originalText.split(" ");
let visibleWordCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const BulletItems = ({ buttonCount }) => {
type="button"
className="glide__bullet"
data-glide-dir={`=${i}`}
aria-label={`Slide view ${i + 1}`}
aria-label={`Go to slide ${i+1} of ${buttonCount}`}
/>
</GaEventWrapper>
);
Expand Down Expand Up @@ -99,7 +99,7 @@ const ImageBulletItems = ({ imageItems, onItemClick = () => null }) => {
decoding="async"
// @ts-ignore

fetchpriority="low"
fetchPriority="low"
/>
</button>
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import React from "react";
*/
const SliderItems = ({ carouselItems }) => {
// Setup carousel items from the carouselItems prop.
const listItems = carouselItems.map(sliderItem => (
<li key={sliderItem.id.toString()} className="glide__slide slider">
const listItems = carouselItems.map((sliderItem, index) => (
<li key={sliderItem.id.toString()} className="glide__slide slider" tabIndex={0}>
<div aria-live="polite" className="sr-only">
<span>{`Slide ${index + 1}`}</span>
{/* <span>{`Slide ${index + 1} of ${carouselItems.length}`}</span> length already announced */}
</div>
{sliderItem.item}
</li>
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function buildConfig(perView = 1, isFullWidth, hasPeek = true, isDraggable) {
return {
type: "slider", // No wrap-around.
focusAt: 0,
bound: true, // Only if type slider with focusAt 0
bound: false, // Only if type slider with focusAt 0 // Set to false for accessibility (all cards should be accessible)
rewind: false, // Only if type slider
gap, // Space between slides... may be impacted by viewport size.
// `keyboard` Left/Right arrow key support for slides - true is default.
Expand Down Expand Up @@ -142,19 +142,19 @@ function setNavButtonGradient(gliderElement, currentIndex, buttonCount) {
imageGalleryNavigation?.classList.add("slider-start");
// Enable/disable prev/next styles. Glide takes care of actual disable.
arrowPrev?.classList.add(cssDisabledClass);
arrowPrev?.setAttribute("aria-disabled", "true");
arrowPrev?.setAttribute("disabled", "true");
arrowNext?.classList.remove(cssDisabledClass);
arrowNext?.setAttribute("aria-disabled", "false");
arrowNext?.removeAttribute("disabled");
} else if (currentIndex >= buttonCount - 1) {
// LAST SLIDE.
// Gradient for end.
gliderTrack?.classList.add("slider-end");
imageGalleryNavigation?.classList.add("slider-end");
// Enable/disable prev/next styles. Glide takes care of actual disable.
arrowPrev?.classList.remove(cssDisabledClass);
arrowPrev?.setAttribute("aria-disabled", "false");
arrowPrev?.removeAttribute("disabled");
arrowNext?.classList.add(cssDisabledClass);
arrowNext?.setAttribute("aria-disabled", "true");
arrowNext?.setAttribute("disabled", "true");
} else {
// MIDDLE SLIDES.
// Gradient for middle.
Expand All @@ -163,8 +163,8 @@ function setNavButtonGradient(gliderElement, currentIndex, buttonCount) {
// Enable/disable prev/next styles. Glide takes care of actual disable.
arrowPrev?.classList.remove(cssDisabledClass);
arrowNext?.classList.remove(cssDisabledClass);
arrowPrev?.setAttribute("aria-disabled", "false");
arrowNext?.setAttribute("aria-disabled", "false");
arrowPrev?.removeAttribute("disabled");
arrowNext?.removeAttribute("disabled");
}
}

Expand Down Expand Up @@ -307,6 +307,108 @@ function setupCaroarousel({
// We use event listeners to clear and set class names to show/hide
// gradients when at the start, middle or end of a slider.
setNavButtonGradient(gliderElement, currentIndex, buttonCount);

// Set the main container with aria-labelledby with the header of the active card
const slides = gliderElement.querySelectorAll(".slider");

// Getting the text from the main header tag
//source: https://stackoverflow.com/questions/67134998/javascript-recursion-to-get-innertext
function getText(node, accumulator) {
if (node.nodeType === 3) {
// 3 == text node
accumulator.push(node.nodeValue);
} else {
for (let child of node.childNodes) getText(child, accumulator);
}
}

let firstActiveCard = 1;
let finalActiveCard = 1;
let totalActiveCards = 0;
let totalCards = slides.length;
let getActiveState = 0; // get first active card

for (let i = 0; i < slides.length; i++) {
const ariaHidden = slides[i].getAttribute("aria-hidden");
if (getActiveState === 1) {
if (i === slides.length - 1) {
if (ariaHidden === "false") {
finalActiveCard = i + 1;
totalActiveCards = finalActiveCard - firstActiveCard + 1;
getActiveState = 2; // done
break;
}
}
if (ariaHidden === "true") {
finalActiveCard = i;
totalActiveCards = finalActiveCard - firstActiveCard + 1;
getActiveState = 2; // done
break;
}
}
if (getActiveState === 0) {
if (ariaHidden === "false") {
firstActiveCard = i + 1;
getActiveState = 1; // get active card count
if (i === slides.length - 1) {
finalActiveCard = firstActiveCard;
totalActiveCards = 1;
getActiveState = 2; // done
}
}
}

if (i === currentIndex) {
// Find the main h tag in the card if one exists
let header;
for (let j = 1; j < 9; j++) {
if (!header) {
header = slides[i].querySelector(`h${j}`);
}
}

if (header) {
let allTexts = [];
getText(slides[i], allTexts);
gliderElement.setAttribute("aria-labelledby", allTexts[0]);
gliderElement.removeAttribute("aria-label");
} else {
gliderElement.setAttribute("aria-label", `Card ${i + 1}`);
gliderElement.removeAttribute("aria-labelledby");
}

}
}

let gliderTrack = gliderElement.querySelector(`.glide__track`);
gliderTrack?.setAttribute("tabIndex", "0");

if (firstActiveCard === finalActiveCard) {
gliderTrack?.setAttribute(
"aria-label",
`Carousel, showing item ${firstActiveCard} of ${totalCards}`
);
} else {
gliderTrack?.setAttribute(
"aria-label",
`Carousel, showing items ${firstActiveCard} to ${finalActiveCard} of ${totalCards}`
// `${totalActiveCards} item carousel, showing items ${firstActiveCard} to ${finalActiveCard} of ${totalCards}`
);
}
gliderTrack?.setAttribute("aria-live", "assertive");

// Update bullet accessibility
const bullets = gliderElement.querySelectorAll(".glide__bullet");
for (let i = 0; i < bullets.length; i++) {
if (i === currentIndex) {
bullets[i].setAttribute("disabled", "");
bullets[i].setAttribute("aria-current", "true");
} else {
bullets[i].removeAttribute("disabled");
bullets[i].setAttribute("aria-current", "false");
}
}

// set the current index
gliderElement.setAttribute("data-current-index", currentIndex);
onItemClick && onItemClick(currentIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ function calcualteViewItems(itemCount, perView) {
buttonCount = itemCount;
if (vw >= 992) {
// Value for lg breakpoint
if (perView >= 2) {
buttonCount = itemCount - 1;
}
if (perView >= 3) {
buttonCount = itemCount - 2;
}
// Removed for accessibility (arrow keys should not skip cards)
// if (perView >= 2) {
// buttonCount = itemCount - 1;
// }
// if (perView >= 3) {
// buttonCount = itemCount - 2;
// }
}

return buttonCount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,18 @@ const BaseCarousel = ({

return (
<div
role={role}
aria-labelledby={ariaLabelledBy}
// role={role}
// role="group"
role="region"
aria-label="Carousel pagination"
className={`glide ${cssClass}`}
id={instanceName}
style={{ width, maxWidth }}
data-remove-side-background={removeSideBackground}
data-image-auto-size={imageAutoSize}
data-has-shadow={hasShadow}
aria-roledescription="carousel"
>
<div className="glide__track" data-glide-el="track">
<SliderItems carouselItems={carouselItems} />
Expand Down
2 changes: 1 addition & 1 deletion packages/unity-react-core/src/components/Image/Image.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const Image = ({
alt,
loading,
decoding,
fetchpriority: fetchPriority, // React attribute bug workaround
fetchPriority: fetchPriority, // React attribute bug workaround
...(cssClasses?.length > 0 && { className: spreadClasses(cssClasses) }),
...(dataTestId && { "data-testid": dataTestId }),
...(width && { width }),
Expand Down