@a11yfred/neighbor 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/lib/rules.js +12 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.4 - 2026-05-13
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
|
|
7
|
+
- **`no-placeholder-only`** — no longer false-positives on `<input>` elements inside a `role="search"` landmark with an accessible name. The input is correctly labeled at the group level in that pattern.
|
|
8
|
+
- **`no-dialog-without-close`** — no longer false-positives on `role="dialog"` elements whose children are passed dynamically (`{children}`). When a close button cannot be statically detected, the rule skips rather than reporting.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
3
12
|
## 1.0.0 - 2026-05-12
|
|
4
13
|
|
|
5
14
|
### Breaking change
|
package/lib/rules.js
CHANGED
|
@@ -845,6 +845,11 @@ export function makeNoPlaceholderOnly(h) {
|
|
|
845
845
|
if (h.getElementName(node) !== 'input') return
|
|
846
846
|
if (!h.hasAttr(node, 'placeholder')) return
|
|
847
847
|
if (h.hasAccessibleName(node)) return
|
|
848
|
+
// An input inside a search landmark with an accessible name is labeled at group level.
|
|
849
|
+
// e.g. <form role="search" aria-label="..."><input placeholder="..." /></form>
|
|
850
|
+
for (const ancestor of h.getAncestors(node)) {
|
|
851
|
+
if (h.getRoleValue(ancestor) === 'search' && h.hasAccessibleName(ancestor)) return
|
|
852
|
+
}
|
|
848
853
|
context.report({ node: h.getAttr(node, 'placeholder'), messageId: 'placeholderOnly' })
|
|
849
854
|
},
|
|
850
855
|
}
|
|
@@ -1206,6 +1211,13 @@ export function makeNoDialogWithoutClose(h) {
|
|
|
1206
1211
|
[h.elementWithChildrenVisitor](node) {
|
|
1207
1212
|
const opening = h.getOpeningElement(node)
|
|
1208
1213
|
if (h.getRoleValue(opening) !== 'dialog') return
|
|
1214
|
+
// If any child is a JSX expression container or spread, children are
|
|
1215
|
+
// passed dynamically and cannot be statically inspected for a close button.
|
|
1216
|
+
const children = node.children ?? []
|
|
1217
|
+
const hasDynamicChildren = children.some(
|
|
1218
|
+
c => c.type === 'JSXExpressionContainer' || c.type === 'JSXSpreadChild'
|
|
1219
|
+
)
|
|
1220
|
+
if (hasDynamicChildren) return
|
|
1209
1221
|
const hasClose = h.getChildOpeningElementsFromWrapper(node).some(child => {
|
|
1210
1222
|
const el = h.getElementName(child)
|
|
1211
1223
|
const role = h.getRoleValue(child)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a11yfred/neighbor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Accessibility linting for a11yfred - ESLint (markup + content), Stylelint (CSS). Won't you be my neighbor?",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@a11yfred/neighbor": "^1.0.
|
|
81
|
+
"@a11yfred/neighbor": "^1.0.3",
|
|
82
82
|
"eslint": "^9.39.4",
|
|
83
83
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
84
84
|
"stylelint": "^17.11.0"
|