Skip to content

Commit

Permalink
Optimized button classification (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
liatnetach authored Feb 16, 2025
1 parent 9a5223c commit f56d922
Show file tree
Hide file tree
Showing 9 changed files with 2,710 additions and 195 deletions.
81 changes: 78 additions & 3 deletions packages/drivers/web-utils/src/getElementCategory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const tagToCategory: Record<
section: "semantic",
form: "semantic",
search: "semantic",
span: (el) => (isInteractiveSemantic(el) ? "button" : undefined),
div: (el) => (isInteractiveSemantic(el) ? "button" : undefined),
};

export const tags = Object.keys(tagToCategory);
Expand All @@ -82,14 +84,47 @@ function getElementCategory(el: Element): ElementCategory | undefined {

const tag = el.tagName.toLowerCase();
const categoryResolver = tagToCategory[tag];
const _isScrollable = isScrollable(el);

if (typeof categoryResolver === "function") {
return categoryResolver(el);
const category = categoryResolver(el);
if (category === undefined) {
return _isScrollable ? "scrollable" : undefined;
}
return category;
} else if (categoryResolver) {
return categoryResolver;
}

return isCustomInteractiveElement(el) ? "button" : undefined;
return isCustomInteractiveElement(el)
? "button"
: _isScrollable
? "scrollable"
: undefined;
}

function isParentMarkedAsButton(el: Element, depth = 0, maxDepth = 2): boolean {
if (depth > maxDepth) {
// Check for depth limit
return false;
}
const parent = el.parentElement;
if (parent) {
const ariaPilotCategory = parent.getAttribute("aria-pilot-category");
const clickableCategories = ["button", "link", "input"]; // Include other clickable categories as needed
if (clickableCategories.includes(ariaPilotCategory ?? "")) {
return true;
}
return isParentMarkedAsButton(parent, depth++, maxDepth);
}
return false;
}

/** Heuristic: Only consider interactive semantics */
function isInteractiveSemantic(el: Element): boolean {
const isInteractive = isCustomInteractiveElement(el);
const isParentAlreadyMarked = isParentMarkedAsButton(el);
return isInteractive && !isParentAlreadyMarked;
}

/** Heuristic: Only consider lists with visible children */
Expand All @@ -105,14 +140,54 @@ function hasTableStructure(el: Element): boolean {
return el.querySelector("thead, tbody, tfoot, tr, td, th") !== null;
}

function hasPointerCursor(el: Element): boolean {
const cursorStyle = window.getComputedStyle(el).cursor;
if (cursorStyle === "pointer") {
const isParentAlreadyMarked = isParentMarkedAsButton(el);
return !isParentAlreadyMarked;
} else {
return false;
}
}

/** Detect web components with button-like behavior */
function isCustomInteractiveElement(el: Element): boolean {
const pointerCursor = hasPointerCursor(el);
return (
el instanceof HTMLElement &&
(el.tabIndex >= 0 ||
el.hasAttribute("onclick") ||
el.getAttribute("role") === "button")
el.getAttribute("role") === "button" ||
pointerCursor)
);
}

// Helper function to check if an element is scrollable
function isScrollable(el: Element): boolean {
// Check for specific CSS classes that indicate scrollability
const scrollableClasses = ["scrollable", "overflow-auto"]; // Customize these classes per your project
for (const className of scrollableClasses) {
if (el.classList.contains(className)) {
return true; // Element is explicitly marked as scrollable
}
}

// Get computed styles
const overflowX = getComputedStyle(el).overflowX;
const overflowY = getComputedStyle(el).overflowY;

// Check if this element has a scrollable style
const isScrollableByStyle =
overflowX === "scroll" ||
overflowY === "scroll" ||
overflowX === "auto" ||
overflowY === "auto";

// Check the element's dimensions and content to avoid false positives
const hasScrollableContent =
el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;

return isScrollableByStyle && hasScrollableContent;
}

export default getElementCategory;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f56d922

Please sign in to comment.