From 4fa1692c1d58c02a70b7a323a91078c92c14dfb6 Mon Sep 17 00:00:00 2001 From: Sven Kummetz Date: Thu, 12 Feb 2026 15:09:07 +0100 Subject: [PATCH] add ability to highlight specific violations via URL parameters --- .env | 3 +- extras/userscript-header.js | 17 +++++++- gulpfile.js | 17 +++++++- package-lock.json | 2 + package.json | 3 +- src/config.ts | 5 +++ src/core/translate.ts | 2 +- src/elements/Accordion/Accordion.tsx | 2 +- .../NodeResultLink/NodeResultLink.tsx | 14 ++++++- src/elements/Violation/Violation.tsx | 40 ++++++++++++++++--- testing/index.html | 6 +++ 11 files changed, 97 insertions(+), 14 deletions(-) diff --git a/.env b/.env index 2ae2162..5dccf79 100644 --- a/.env +++ b/.env @@ -5,4 +5,5 @@ THEME_NEUTRAL="#323130" THEME_NEUTRAL_LIGHT="#656462" THEME_NEUTRAL_DARK="#1F1E1D" AXE_MIN_URL="https://tools.caat.report/axe-core/axe.min.js" -AXE_LOCALE_URL="https://tools.caat.report/axe-core/locales/i18n('baat.lang').json" \ No newline at end of file +AXE_LOCALE_URL="https://tools.caat.report/axe-core/locales/i18n('baat.lang').json" +BAAT_HOMEPAGE_URL="https://tools.caat.report/baat/" \ No newline at end of file diff --git a/extras/userscript-header.js b/extras/userscript-header.js index 3d3d912..f9afa3d 100644 --- a/extras/userscript-header.js +++ b/extras/userscript-header.js @@ -5,6 +5,21 @@ // @description Run axe-core from the browser // @author Mindscreen GmbH // @match *://*/* -// @grant none +// @grant GM_registerMenuCommand +// @homepage <%= homepage %> // ==/UserScript== +(function() { + 'use strict'; + + function run() { +// baat + } + + GM_registerMenuCommand('Run BAAT', run); + + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has('baat-show-id')) { + run(); + } +})(); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index ad14284..919d52f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -7,6 +7,7 @@ const gulpHtmlI18n = require('gulp-html-i18n'); const {nodeResolve} = require('@rollup/plugin-node-resolve') const externalGlobals = require("rollup-plugin-external-globals") const { default: minifyHTML } = require('rollup-plugin-minify-html-literals') +const through2 = require('through2') const header = require('gulp-header') const footer = require('gulp-footer') const pkg = require('./package.json') @@ -97,11 +98,23 @@ gulp.task('rollup-userscript', function () { { file: 'userscript.js', name: 'baat', - format: 'iife', + format: 'es', }, ], })) - .pipe(header(fs.readFileSync('extras/userscript-header.js', 'utf8'), {version: pkg.version})) + .pipe(through2.obj(function (file, enc, cb) { + const template = fs.readFileSync('extras/userscript-header.js', 'utf8') + .replace('<%= version %>', pkg.version) + .replace('<%= homepage %=>', String(process.env.BAAT_HOMEPAGE_URL)); + const bundledCode = file.contents.toString() + .split('\n') + .map(line => line ? ' ' + line : line) + .join('\n'); + file.contents = Buffer.from( + template.replace('// baat', bundledCode) + ); + cb(null, file); + })) .pipe(gulp.dest('./intermediate/bundled/')); }); gulp.task('rollup-bookmarklet', function () { diff --git a/package-lock.json b/package-lock.json index 602de37..a77b20d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "gulp-uglify": "^3.0.2", "rollup-plugin-external-globals": "^0.6.1", "rollup-plugin-minify-html-literals": "^1.2.6", + "through2": "^4.0.2", "typescript": "^4.4.3" } }, @@ -6362,6 +6363,7 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "dev": true, + "license": "MIT", "dependencies": { "readable-stream": "3" } diff --git a/package.json b/package.json index 9c23b07..56e6dfd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "baat", - "version": "1.4.3", + "version": "1.4.4", "description": "Bookmarklet for running axe-core tests directly in the Browser", "main": "index.js", "scripts": { @@ -34,6 +34,7 @@ "gulp-uglify": "^3.0.2", "rollup-plugin-external-globals": "^0.6.1", "rollup-plugin-minify-html-literals": "^1.2.6", + "through2": "^4.0.2", "typescript": "^4.4.3" }, "dependencies": { diff --git a/src/config.ts b/src/config.ts index b8c0b46..2baffe3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -31,4 +31,9 @@ export const localStorageKeys = { settings: 'baat_settings', history: 'baat_history', view: 'baat_view', +} + +export const urlParams = { + id: 'baat-show-id', + target: 'baat-show-target', } \ No newline at end of file diff --git a/src/core/translate.ts b/src/core/translate.ts index f8aa100..eb4e1f3 100644 --- a/src/core/translate.ts +++ b/src/core/translate.ts @@ -1,6 +1,6 @@ import * as axe from 'axe-core'; -export const translateImpact = (impact: axe.ImpactValue) : string => { +export const translateImpact = (impact: axe.ImpactValue | 'none') : string => { switch (impact) { case 'critical': return "i18n('baat.impact.critical')"; case 'serious': return "i18n('baat.impact.serious')"; diff --git a/src/elements/Accordion/Accordion.tsx b/src/elements/Accordion/Accordion.tsx index 39751e9..baf2f69 100644 --- a/src/elements/Accordion/Accordion.tsx +++ b/src/elements/Accordion/Accordion.tsx @@ -114,7 +114,7 @@ export class Accordion extends BaseHTMLElement implements IA } } - static get observedAttributes(): (keyof IAccordionAccessor)[] { return [ 'folded', 'fixed', 'nestedRoot', 'textColor' ] } + static get observedAttributes(): (keyof IAccordionAccessor)[] { return [ 'folded', 'fixed', 'nestedRoot', 'textColor', 'color' ] } constructor() { super() diff --git a/src/elements/NodeResultLink/NodeResultLink.tsx b/src/elements/NodeResultLink/NodeResultLink.tsx index f41fcd6..f8f93d2 100644 --- a/src/elements/NodeResultLink/NodeResultLink.tsx +++ b/src/elements/NodeResultLink/NodeResultLink.tsx @@ -7,7 +7,7 @@ import { removeAllChildren } from '../../util/dom' import { baatSymbol } from '../../core/BAAT' import { BAATEvent, HighlightElement, SettingsChanged } from '../../types' import { Icon } from '../Icon/Icon' -import { settingNames } from "../../config"; +import {settingNames, urlParams} from '../../config'; import { getElementFromNodeResult, getNameFromNodeResult } from '../../util/axe'; const padding = `${theme.sizing.relative.tiny} ${theme.sizing.relative.smaller}`; @@ -39,6 +39,9 @@ const styles = css` button:hover { background-color: ${theme.palette.grayLight}; } + button.highlighted { + background-color: ${theme.palette.none}; + } ` interface INodeLinkAccessor { @@ -75,6 +78,15 @@ export class NodeResultLink extends BaseHTMLElement implement this.buttonRef.value.appendChild() this.buttonRef.value.appendChild(document.createTextNode(name)) + + const searchParams = new URLSearchParams(window.location.search); + const target = typeof this.result?.target[0] === 'string' + ? this.result.target[0] + : ""; + + if (searchParams.has(urlParams.target) && searchParams.get(urlParams.target) === target) { + this.buttonRef.value.classList.add('highlighted') + } } initialize() { diff --git a/src/elements/Violation/Violation.tsx b/src/elements/Violation/Violation.tsx index 345541f..25c6620 100644 --- a/src/elements/Violation/Violation.tsx +++ b/src/elements/Violation/Violation.tsx @@ -6,17 +6,16 @@ import { css } from '../../util/taggedString' import { theme } from '../../theme' import { baact, createRef } from '../../../baact/baact' import { Accordion } from '../Accordion/Accordion' -import { NodeResult, Result } from '../../types' +import {BAATEvent, HighlightElement, NodeResult, Result} from '../../types'; import { hideHighlight, showHighlight } from '../../core/highlight' import { Icon } from '../Icon/Icon' import { baatSymbol } from "../../core/BAAT"; -import { settingNames } from "../../config"; +import {settingNames, urlParams} from '../../config'; import { getElementFromNodeResult, getNameFromNodeResult, transformInfoToHTMLLists } from '../../util/axe'; import { isHidden } from '../../util/dom'; import { partition } from '../../util/array'; import { link } from '../../styles/link'; -import {capitlizeFirstLetter} from '../../util/string'; -import {translateImpact} from '../../core/translate'; +import { translateImpact } from '../../core/translate'; const padding = `${theme.sizing.relative.tiny} ${theme.sizing.relative.smaller}`; @@ -107,6 +106,9 @@ const styles = css` #hideButton { margin-top: 1.25rem; } + .highlighted { + background-color: ${theme.palette.grayLight}; + } `; interface IViolationAccessor { @@ -133,6 +135,7 @@ export class Violation extends BaseHTMLElement implements IV private infoRef = createRef() private highlightTitleRef = createRef() private otherTitleRef = createRef() + private accordionRef = createRef() attributeChangedCallback(name: T, oldValue: IViolationAccessor[T], newValue: IViolationAccessor[T]) { switch (name) { @@ -161,7 +164,7 @@ export class Violation extends BaseHTMLElement implements IV this.infoRef.value.innerHTML = ''; } else { this.titleRef.value.innerText = this.result.help; - this.impactRef.value.innerText = translateImpact(this.result.impact); + this.impactRef.value.innerText = translateImpact(this.result.impact ?? 'none'); this.impactRef.value.className = "chip "+this.result.impact; this.descriptionRef.value.innerText = this.result.description; @@ -190,6 +193,31 @@ export class Violation extends BaseHTMLElement implements IV if (this.result.helpUrl && this.result.helpUrl !== "") { this.linkRef.value.innerHTML = `

i18n('baat.violation.helpForError')

${this.result.id} i18n('baat.violation.onDequeUniversity')` } + + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has(urlParams.id) && searchParams.get(urlParams.id) === this.result.id) { + this.accordionRef.value.setAttribute('folded', false); + window.setTimeout(() => { + this.accordionRef.value.scrollIntoView(); + }, 0); + this.accordionRef.value.focus(); + this.accordionRef.value.setAttribute('color', theme.palette.none); + + const result = (searchParams.has(urlParams.target) && + linkedResults.find((result) => { + const target = typeof result.target[0] === 'string' + ? result.target[0] + : ""; + return target === searchParams.get(urlParams.target) + })) || + linkedResults[0]; + + const element = getElementFromNodeResult(result) + + if (element) { + window[baatSymbol].dispatchEvent(new CustomEvent(BAATEvent.HighlightElement, {detail: {element: element}})); + } + } } } @@ -205,7 +233,7 @@ export class Violation extends BaseHTMLElement implements IV } this.shadowRoot?.appendChild( - +

diff --git a/testing/index.html b/testing/index.html index ada051b..f9a31db 100644 --- a/testing/index.html +++ b/testing/index.html @@ -52,8 +52,14 @@ langSelect.value = lang; langSelect.ariaLabel = 'BAAT Language'; + const searchParams = new URLSearchParams(window.location.search) + if (!searchParams.has('baat-show-id')) + window.history.replaceState({}, '', `?baat-show-id=empty`); + document.getElementById('controls').appendChild(langSelect); }); + + function GM_registerMenuCommand() {}