Skip to content

Commit 24b6193

Browse files
authored
Refactor img-has-alt to alt-text (#220)
1 parent 5be6cd6 commit 24b6193

16 files changed

+613
-309
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ You can also enable all the recommended rules at once. Add `plugin:jsx-a11y/reco
9393
## Supported Rules
9494

9595
- [accessible-emoji](docs/rules/accessible-emoji.md): Enforce emojis are wrapped in <span> and provide screenreader access.
96+
- [alt-text](docs/rules/alt-text.md): Enforce all elements that require alternative text have meaningful information to relay back to end user.
9697
- [anchor-has-content](docs/rules/anchor-has-content.md): Enforce all anchors to contain accessible content.
9798
- [aria-activedescendant-has-tabindex](docs/rules/aria-activedescendant-has-tabindex.md): Enforce elements with aria-activedescendant are tabbable.
9899
- [aria-props](docs/rules/aria-props.md): Enforce all `aria-*` props are valid.
@@ -104,7 +105,6 @@ You can also enable all the recommended rules at once. Add `plugin:jsx-a11y/reco
104105
- [href-no-hash](docs/rules/href-no-hash.md): Enforce an anchor element's `href` prop value is not just `#`.
105106
- [html-has-lang](docs/rules/html-has-lang.md): Enforce `<html>` element has `lang` prop.
106107
- [iframe-has-title](docs/rules/iframe-has-title.md): Enforce iframe elements have a title attribute.
107-
- [img-has-alt](docs/rules/img-has-alt.md): Enforce that `<img>` JSX elements use the `alt` prop.
108108
- [img-redundant-alt](docs/rules/img-redundant-alt.md): Enforce `<img>` alt prop does not contain the word "image", "picture", or "photo".
109109
- [interactive-supports-focus](docs/rules/interactive-supports-focus.md): Enforce that elements with interactive handlers like `onClick` must be focusable.
110110
- [label-has-for](docs/rules/label-has-for.md): Enforce that `<label>` elements have the `htmlFor` prop.

__mocks__/JSXElementMock.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
export default function JSXElementMock (tagName, attributes) {
1+
export default function JSXElementMock(tagName, attributes, children = []) {
22
return {
33
type: 'JSXElement',
44
openingElement: {
55
type: 'JSXOpeningElement',
66
name: {
77
type: 'JSXIdentifier',
8-
name: tagName
8+
name: tagName,
99
},
1010
attributes,
1111
},
12+
children,
1213
};
1314
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export default function JSXExpressionContainerMock (exp) {
1+
export default function JSXExpressionContainerMock(exp) {
22
return {
33
type: 'JSXExpressionContainer',
44
expression: exp,
55
};
6-
};
6+
}

__mocks__/genInteractives.js

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,37 @@ const domElements = [...dom.keys()];
1313
const roleNames = [...roles.keys()];
1414

1515
const interactiveElementsMap = {
16-
a: [{prop: 'href', value: '#'}],
17-
area: [{prop: 'href', value: '#'}],
16+
a: [{ prop: 'href', value: '#' }],
17+
area: [{ prop: 'href', value: '#' }],
1818
audio: [],
1919
button: [],
2020
canvas: [],
2121
embed: [],
2222
link: [],
2323
input: [],
24-
'input[type=\"button\"]': [{prop: 'type', value: 'button'}],
25-
'input[type=\"checkbox\"]': [{prop: 'type', value: 'checkbox'}],
26-
'input[type=\"color\"]': [{prop: 'type', value: 'color'}],
27-
'input[type=\"date\"]': [{prop: 'type', value: 'date'}],
28-
'input[type=\"datetime\"]': [{prop: 'type', value: 'datetime'}],
29-
'input[type=\"datetime\"]': [{prop: 'type', value: 'datetime'}],
30-
'input[type=\"email\"]': [{prop: 'type', value: 'email'}],
31-
'input[type=\"file\"]': [{prop: 'type', value: 'file'}],
32-
'input[type=\"image\"]': [{prop: 'type', value: 'image'}],
33-
'input[type=\"month\"]': [{prop: 'type', value: 'month'}],
34-
'input[type=\"number\"]': [{prop: 'type', value: 'number'}],
35-
'input[type=\"password\"]': [{prop: 'type', value: 'password'}],
36-
'input[type=\"radio\"]': [{prop: 'type', value: 'radio'}],
37-
'input[type=\"range\"]': [{prop: 'type', value: 'range'}],
38-
'input[type=\"reset\"]': [{prop: 'type', value: 'reset'}],
39-
'input[type=\"search\"]': [{prop: 'type', value: 'search'}],
40-
'input[type=\"submit\"]': [{prop: 'type', value: 'submit'}],
41-
'input[type=\"tel\"]': [{prop: 'type', value: 'tel'}],
42-
'input[type=\"text\"]': [{prop: 'type', value: 'text'}],
43-
'input[type=\"time\"]': [{prop: 'type', value: 'time'}],
44-
'input[type=\"url\"]': [{prop: 'type', value: 'url'}],
45-
'input[type=\"week\"]': [{prop: 'type', value: 'week'}],
46-
menuitem:[],
24+
'input[type=\"button\"]': [{ prop: 'type', value: 'button' }],
25+
'input[type=\"checkbox\"]': [{ prop: 'type', value: 'checkbox' }],
26+
'input[type=\"color\"]': [{ prop: 'type', value: 'color' }],
27+
'input[type=\"date\"]': [{ prop: 'type', value: 'date' }],
28+
'input[type=\"datetime\"]': [{ prop: 'type', value: 'datetime' }],
29+
'input[type=\"datetime\"]': [{ prop: 'type', value: 'datetime' }],
30+
'input[type=\"email\"]': [{ prop: 'type', value: 'email' }],
31+
'input[type=\"file\"]': [{ prop: 'type', value: 'file' }],
32+
'input[type=\"image\"]': [{ prop: 'type', value: 'image' }],
33+
'input[type=\"month\"]': [{ prop: 'type', value: 'month' }],
34+
'input[type=\"number\"]': [{ prop: 'type', value: 'number' }],
35+
'input[type=\"password\"]': [{ prop: 'type', value: 'password' }],
36+
'input[type=\"radio\"]': [{ prop: 'type', value: 'radio' }],
37+
'input[type=\"range\"]': [{ prop: 'type', value: 'range' }],
38+
'input[type=\"reset\"]': [{ prop: 'type', value: 'reset' }],
39+
'input[type=\"search\"]': [{ prop: 'type', value: 'search' }],
40+
'input[type=\"submit\"]': [{ prop: 'type', value: 'submit' }],
41+
'input[type=\"tel\"]': [{ prop: 'type', value: 'tel' }],
42+
'input[type=\"text\"]': [{ prop: 'type', value: 'text' }],
43+
'input[type=\"time\"]': [{ prop: 'type', value: 'time' }],
44+
'input[type=\"url\"]': [{ prop: 'type', value: 'url' }],
45+
'input[type=\"week\"]': [{ prop: 'type', value: 'week' }],
46+
menuitem: [],
4747
option: [],
4848
select: [],
4949
// Whereas ARIA makes a distinction between cell and gridcell, the AXObject
@@ -111,7 +111,7 @@ const indeterminantInteractiveElementsMap = domElements
111111
.reduce(
112112
(
113113
accumulator: {[key: string]: Array<any>},
114-
name: string
114+
name: string,
115115
): {[key: string]: Array<any>} => {
116116
accumulator[name] = [];
117117
return accumulator;
@@ -122,7 +122,7 @@ const indeterminantInteractiveElementsMap = domElements
122122
Object.keys(interactiveElementsMap)
123123
.concat(Object.keys(nonInteractiveElementsMap))
124124
.forEach(
125-
(name: string) => delete indeterminantInteractiveElementsMap[name]
125+
(name: string) => delete indeterminantInteractiveElementsMap[name],
126126
);
127127

128128
const abstractRoles = roleNames
@@ -152,93 +152,93 @@ const nonInteractiveRoles = roleNames
152152
// aria-activedescendant, thus in practice we treat it as a widget.
153153
.filter(role => !['toolbar'].includes(role));
154154

155-
export function genElementSymbol (
155+
export function genElementSymbol(
156156
openingElement: Object,
157157
) {
158158
return openingElement.name.name + (
159159
(openingElement.attributes.length > 0)
160160
? `${openingElement.attributes.map(
161-
attr => `[${attr.name.name}=\"${attr.value.value}\"]` ).join('')
161+
attr => `[${attr.name.name}=\"${attr.value.value}\"]`).join('')
162162
}`
163163
: ''
164164
);
165-
};
165+
}
166166

167-
export function genInteractiveElements () {
167+
export function genInteractiveElements() {
168168
return Object.keys(interactiveElementsMap)
169-
.map(elementSymbol => {
169+
.map((elementSymbol) => {
170170
const bracketIndex = elementSymbol.indexOf('[');
171171
let name = elementSymbol;
172172
if (bracketIndex > -1) {
173173
name = elementSymbol.slice(0, bracketIndex);
174174
}
175175
const attributes = interactiveElementsMap[elementSymbol].map(
176-
({prop, value}) => JSXAttributeMock(prop, value)
176+
({ prop, value }) => JSXAttributeMock(prop, value),
177177
);
178178
return JSXElementMock(name, attributes);
179179
});
180180
}
181181

182-
export function genInteractiveRoleElements () {
182+
export function genInteractiveRoleElements() {
183183
return [
184184
...interactiveRoles,
185185
'button article',
186186
'fakerole button article',
187187
].map(
188188
value => JSXElementMock('div', [
189-
JSXAttributeMock('role', value)
190-
])
189+
JSXAttributeMock('role', value),
190+
]),
191191
);
192192
}
193193

194-
export function genNonInteractiveElements () {
194+
export function genNonInteractiveElements() {
195195
return Object.keys(nonInteractiveElementsMap)
196-
.map(elementSymbol => {
196+
.map((elementSymbol) => {
197197
const bracketIndex = elementSymbol.indexOf('[');
198198
let name = elementSymbol;
199199
if (bracketIndex > -1) {
200200
name = elementSymbol.slice(0, bracketIndex);
201201
}
202202
const attributes = nonInteractiveElementsMap[elementSymbol].map(
203-
({prop, value}) => JSXAttributeMock(prop, value)
203+
({ prop, value }) => JSXAttributeMock(prop, value),
204204
);
205205
return JSXElementMock(name, attributes);
206206
});
207207
}
208208

209-
export function genNonInteractiveRoleElements () {
209+
export function genNonInteractiveRoleElements() {
210210
return [
211211
...nonInteractiveRoles,
212212
'article button',
213213
'fakerole article button',
214214
].map(
215215
value => JSXElementMock('div', [
216-
JSXAttributeMock('role', value)
217-
])
216+
JSXAttributeMock('role', value),
217+
]),
218218
);
219219
}
220220

221-
export function genAbstractRoleElements () {
221+
export function genAbstractRoleElements() {
222222
return abstractRoles.map(
223223
value => JSXElementMock('div', [
224-
JSXAttributeMock('role', value)
225-
])
224+
JSXAttributeMock('role', value),
225+
]),
226226
);
227-
};
227+
}
228228

229-
export function genNonAbstractRoleElements () {
229+
export function genNonAbstractRoleElements() {
230230
return nonAbstractRoles.map(
231231
value => JSXElementMock('div', [
232-
JSXAttributeMock('role', value)
233-
])
232+
JSXAttributeMock('role', value),
233+
]),
234234
);
235-
};
235+
}
236236

237-
export function genIndeterminantInteractiveElements () {
237+
export function genIndeterminantInteractiveElements() {
238238
return Object.keys(indeterminantInteractiveElementsMap)
239-
.map(name => {
239+
.map((name) => {
240240
const attributes = indeterminantInteractiveElementsMap[name].map(
241-
({prop, value}) => JSXAttributeMock(prop, value)
241+
({ prop, value }) => JSXAttributeMock(prop, value),
242242
);
243243
return JSXElementMock(name, attributes);
244244
});

0 commit comments

Comments
 (0)