eslint-plugin-smarthr 1.7.0 → 1.8.1
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 +17 -0
- package/package.json +3 -3
- package/rules/a11y-anchor-has-href-attribute/index.js +1 -1
- package/rules/a11y-delegate-element-has-role-presentation/index.js +21 -21
- package/rules/a11y-heading-in-sectioning-content/index.js +27 -9
- package/rules/best-practice-for-layouts/index.js +5 -1
- package/rules/format-translate-component/index.js +19 -19
- package/test/a11y-delegate-element-has-role-presentation.js +2 -2
- package/test/a11y-heading-in-sectioning-content.js +7 -2
- package/test/best-practice-for-layouts.js +3 -4
- package/test/format-translate-component.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [1.8.1](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.8.0...eslint-plugin-smarthr-v1.8.1) (2025-06-12)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* a11y-anchor-has-href-attribute での特殊分岐条件を react-router-dom から react-router に変更 ([#678](https://github.com/kufu/tamatebako/issues/678)) ([5941384](https://github.com/kufu/tamatebako/commit/59413842b5b7e5ca6ee88a511792ae6a81be988b))
|
|
11
|
+
* a11y-delegate-element-has-role-presentationでインタラクティブな要素を判定する際、コンポーネント名が複数形でも許容する ([#679](https://github.com/kufu/tamatebako/issues/679)) ([67567bd](https://github.com/kufu/tamatebako/commit/67567bde757624ed46649fb0401e24dc904865ba))
|
|
12
|
+
* a11y-heading-in-sectioning-contentでNavにaria-labelが設定されている場合を許容する ([#680](https://github.com/kufu/tamatebako/issues/680)) ([12f26be](https://github.com/kufu/tamatebako/commit/12f26be9d461d1580711263ae974ed948c74fc1d))
|
|
13
|
+
|
|
14
|
+
## [1.8.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.7.0...eslint-plugin-smarthr-v1.8.0) (2025-06-09)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* best-practice-for-layoutsで <any>{hoge}</any> など子要素が変数のみの場合を許容する ([#668](https://github.com/kufu/tamatebako/issues/668)) ([7d03ff1](https://github.com/kufu/tamatebako/commit/7d03ff150833f673b9c2afe8e86adfdaee150d72))
|
|
20
|
+
* format-translate-component rule で RangeSeparator を許容する ([#667](https://github.com/kufu/tamatebako/issues/667)) ([951bee2](https://github.com/kufu/tamatebako/commit/951bee2f376ddf48c5e9a0b13eb50b2daeeec7f9))
|
|
21
|
+
|
|
5
22
|
## [1.7.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v1.6.0...eslint-plugin-smarthr-v1.7.0) (2025-06-02)
|
|
6
23
|
|
|
7
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-smarthr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"author": "SmartHR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A sharable ESLint plugin for SmartHR",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"json5": "^2.2.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"typescript-eslint": "^8.33.
|
|
29
|
+
"typescript-eslint": "^8.33.1"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"eslint": "^9"
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"eslintplugin",
|
|
38
38
|
"smarthr"
|
|
39
39
|
],
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "18fc771c7e65e784d224990f2ce857c150cd69e2"
|
|
41
41
|
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
const INTERACTIVE_COMPONENT_NAMES = `(${[
|
|
2
|
-
'(B|b)utton',
|
|
3
|
-
'(Check|Combo)(B|b)ox',
|
|
4
|
-
'(Date(timeLocal)?|Time|Month|Wareki)Picker',
|
|
5
|
-
'(I|i)nput(File)?',
|
|
6
|
-
'(S|s)elect',
|
|
7
|
-
'(T|t)extarea',
|
|
8
|
-
'(ActionDialogWith|RemoteDialog)Trigger',
|
|
9
|
-
'AccordionPanel',
|
|
2
|
+
'(B|b)utton(s)?',
|
|
3
|
+
'(Check|Combo)(B|b)ox(es|s)?',
|
|
4
|
+
'(Date(timeLocal)?|Time|Month|Wareki)Picker(s)?',
|
|
5
|
+
'(I|i)nput(File)?(s)?',
|
|
6
|
+
'(S|s)elect(s)?',
|
|
7
|
+
'(T|t)extarea(s)?',
|
|
8
|
+
'(ActionDialogWith|RemoteDialog)Trigger(s)?',
|
|
9
|
+
'AccordionPanel(s)?',
|
|
10
10
|
'^a',
|
|
11
11
|
'Anchor',
|
|
12
|
-
'Link',
|
|
13
|
-
'DropZone',
|
|
14
|
-
'Field(S|s)et',
|
|
15
|
-
'FilterDropdown',
|
|
16
|
-
'(F|f)orm(Control|Group|Dialog)?',
|
|
17
|
-
'Pagination',
|
|
18
|
-
'RadioButton(Panel)?',
|
|
19
|
-
'RemoteTrigger(.+)Dialog',
|
|
20
|
-
'RightFixedNote',
|
|
21
|
-
'SegmentedControl',
|
|
22
|
-
'SideNav',
|
|
23
|
-
'Switch',
|
|
24
|
-
'TabItem',
|
|
12
|
+
'Link(s)?',
|
|
13
|
+
'DropZone(s)?',
|
|
14
|
+
'Field(S|s)et(s)?',
|
|
15
|
+
'FilterDropdown(s)?',
|
|
16
|
+
'(F|f)orm(Control|Group|Dialog)?(s)?',
|
|
17
|
+
'Pagination(s)?',
|
|
18
|
+
'RadioButton(Panel)?(s)?',
|
|
19
|
+
'RemoteTrigger(.+)Dialog(s)?',
|
|
20
|
+
'RightFixedNote(s)?',
|
|
21
|
+
'SegmentedControl(s)?',
|
|
22
|
+
'SideNav(s)?',
|
|
23
|
+
'Switch(s)?',
|
|
24
|
+
'TabItem(s)?',
|
|
25
25
|
].join('|')})$`
|
|
26
26
|
const INTERACTIVE_ON_REGEX = /^on(Change|Input|Focus|Blur|(Double)?Click|Key(Down|Up|Press)|Mouse(Enter|Over|Down|Up|Leave)|Select|Submit)$/
|
|
27
27
|
const MEANED_ROLE_REGEX = /^(combobox|group|slider|toolbar)$/
|
|
@@ -10,9 +10,9 @@ const ignoreCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
|
|
|
10
10
|
const noHeadingTagNamesRegex = /^(span|legend)$/
|
|
11
11
|
const ignoreHeadingCheckParentTypeRegex = /^(Program|ExportNamedDeclaration)$/
|
|
12
12
|
const headingAttributeRegex = /^(heading|title)$/
|
|
13
|
+
const ariaLabelRegex = /^aria-label(ledby)?$/
|
|
13
14
|
|
|
14
15
|
const includeSectioningAsAttr = (a) => asRegex.test(a.name?.name) && bareTagRegex.test(a.value.value)
|
|
15
|
-
const findHeadingAttribute = (a) => headingAttributeRegex.test(a.name?.name || '')
|
|
16
16
|
|
|
17
17
|
const headingMessage = `smarthr-ui/Headingと紐づく内容の範囲(アウトライン)が曖昧になっています。
|
|
18
18
|
- smarthr-uiのArticle, Aside, Nav, SectionのいずれかでHeadingコンポーネントと内容をラップしてHeadingに対応する範囲を明確に指定してください。`
|
|
@@ -250,23 +250,41 @@ module.exports = {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
} else if (!node.selfClosing) {
|
|
253
|
-
const isSection =
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
const isSection = elementName.match(sectioningRegex)
|
|
254
|
+
let isNav = false
|
|
255
|
+
|
|
256
|
+
if (isSection) {
|
|
257
|
+
isNav = isSection[1] === 'Nav'
|
|
258
|
+
|
|
259
|
+
for (let i = 0; i < node.attributes.length; i++) {
|
|
260
|
+
const attrName = node.attributes[i].name?.name || ''
|
|
261
|
+
|
|
262
|
+
if (
|
|
263
|
+
// HINT: SectioningContent系コンポーネントの拡張の場合、title, heading属性などにHeadingのテキストが仕込まれている場合がある
|
|
264
|
+
// 対象属性を持っている場合はcorrectとして扱う
|
|
265
|
+
headingAttributeRegex.test(attrName) ||
|
|
266
|
+
// HINT: Navかつaria-labelを持っている場合も許容する
|
|
267
|
+
isNav && ariaLabelRegex.test(attrName)
|
|
268
|
+
) {
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
}
|
|
259
272
|
}
|
|
260
273
|
|
|
261
274
|
const layoutSectionAsAttr = !isSection && layoutComponentRegex.test(elementName) ? node.attributes.find(includeSectioningAsAttr) : null
|
|
262
275
|
|
|
263
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
(isSection || layoutSectionAsAttr) &&
|
|
278
|
+
!searchBubbleUpSections(node.parent.parent) &&
|
|
279
|
+
!forInSearchChildren(node.parent.children)
|
|
280
|
+
) {
|
|
264
281
|
context.report({
|
|
265
282
|
node,
|
|
266
283
|
message: `${isSection ? elementName : `<${elementName} ${layoutSectionAsAttr.name.name}="${layoutSectionAsAttr.value.value}">`} はHeading要素を含んでいません。
|
|
267
284
|
- SectioningContentはHeadingを含むようにマークアップする必要があります
|
|
268
285
|
- ${elementName}に設定しているいずれかの属性がHeading,もしくはHeadingのテキストに該当する場合、その属性の名称を ${headingAttributeRegex.toString()} にマッチする名称に変更してください
|
|
269
|
-
- Headingにするべき適切な文字列が存在しない場合、 ${isSection ? `${elementName} は削除するか、SectioningContentではない要素に差し替えてください` : `${layoutSectionAsAttr.name.name}="${layoutSectionAsAttr.value.value}"を削除、もしくは別の要素に変更してください`}
|
|
286
|
+
- Headingにするべき適切な文字列が存在しない場合、 ${isSection ? `${elementName} は削除するか、SectioningContentではない要素に差し替えてください` : `${layoutSectionAsAttr.name.name}="${layoutSectionAsAttr.value.value}"を削除、もしくは別の要素に変更してください`}${isNav ? `
|
|
287
|
+
- nav要素の場合、aria-label、もしくはaria-labelledby属性を設定し、どんなナビゲーションなのかがわかる名称を設定してください` : ''}`,
|
|
270
288
|
})
|
|
271
289
|
}
|
|
272
290
|
}
|
|
@@ -24,6 +24,11 @@ const searchChildren = (node) => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
switch(node.type) {
|
|
27
|
+
case 'MemberExpression':
|
|
28
|
+
return searchChildren(node.object)
|
|
29
|
+
// {hoge} や {hoge.fuga} の場合、許容する
|
|
30
|
+
case 'Identifier':
|
|
31
|
+
return false
|
|
27
32
|
case 'JSXExpressionContainer':
|
|
28
33
|
case 'ChainExpression':
|
|
29
34
|
return searchChildren(node.expression)
|
|
@@ -101,7 +106,6 @@ module.exports = {
|
|
|
101
106
|
return
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
|
|
105
109
|
if (searchChildren(children[0])) {
|
|
106
110
|
context.report({
|
|
107
111
|
node,
|
|
@@ -14,6 +14,10 @@ const SCHEMA = [
|
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
const NOOP = () => {}
|
|
17
|
+
const findDangerouslySetInnerHTMLAttr = (a) => a.name.name === 'dangerouslySetInnerHTML'
|
|
18
|
+
|
|
19
|
+
const WHITESPACE_REGEX = /(\s|\n)+/g
|
|
20
|
+
const ALLOW_ELM_REGEX = /^(br|RangeSeparator)$/
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* @type {import('@typescript-eslint/utils').TSESLint.RuleModule<''>}
|
|
@@ -46,43 +50,39 @@ module.exports = {
|
|
|
46
50
|
// HINT: 翻訳コンポーネントはテキストとbrのみ許容する
|
|
47
51
|
if (node.name.name === componentName) {
|
|
48
52
|
let existValidChild = false
|
|
49
|
-
let existNotBrElement = false
|
|
50
53
|
|
|
51
|
-
node.parent.children.
|
|
54
|
+
for (let i = 0; i < node.parent.children.length; i++) {
|
|
55
|
+
const c = node.parent.children[i]
|
|
56
|
+
|
|
52
57
|
switch (c.type) {
|
|
53
58
|
case 'JSXText':
|
|
54
59
|
// HINT: 空白と改行のみの場合はテキストが存在する扱いにはしない
|
|
55
|
-
if (c.value.replace(
|
|
56
|
-
|
|
60
|
+
if (existValidChild || !c.value.replace(WHITESPACE_REGEX, '')) {
|
|
61
|
+
break
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
|
|
64
|
+
existValidChild = true
|
|
60
65
|
case 'JSXExpressionContainer':
|
|
61
66
|
// TODO 変数がstringのみか判定できるなら対応したい
|
|
62
67
|
existValidChild = true
|
|
63
68
|
|
|
64
69
|
break
|
|
65
70
|
case 'JSXElement':
|
|
66
|
-
if (c.openingElement.name.name
|
|
67
|
-
|
|
71
|
+
if (ALLOW_ELM_REGEX.test(c.openingElement.name.name)) {
|
|
72
|
+
break
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const message = (() => {
|
|
75
|
-
if (existNotBrElement) {
|
|
76
|
-
return `${componentName} 内では <br /> 以外のタグは使えません`
|
|
77
|
-
} else if (!existValidChild && !node.attributes.some((a) => a.name.name === 'dangerouslySetInnerHTML')) {
|
|
78
|
-
return `${componentName} 内には必ずテキストを設置してください`
|
|
75
|
+
return context.report({
|
|
76
|
+
node,
|
|
77
|
+
message: `${componentName} 内では <br />, <RangeSeparator /> 以外のタグは使えません`,
|
|
78
|
+
});
|
|
79
79
|
}
|
|
80
|
-
}
|
|
80
|
+
}
|
|
81
81
|
|
|
82
|
-
if (
|
|
82
|
+
if (!existValidChild && !node.attributes.some(findDangerouslySetInnerHTMLAttr)) {
|
|
83
83
|
context.report({
|
|
84
84
|
node,
|
|
85
|
-
message
|
|
85
|
+
message: `${componentName} 内には必ずテキストを設置してください`,
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -11,7 +11,7 @@ const ruleTester = new RuleTester({
|
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
const defaultInteractiveMessage = '((B|b)utton
|
|
14
|
+
const defaultInteractiveMessage = '((B|b)utton(s)?|(Check|Combo)(B|b)ox(es|s)?|(Date(timeLocal)?|Time|Month|Wareki)Picker(s)?|(I|i)nput(File)?(s)?|(S|s)elect(s)?|(T|t)extarea(s)?|(ActionDialogWith|RemoteDialog)Trigger(s)?|AccordionPanel(s)?|^a|Anchor|Link(s)?|DropZone(s)?|Field(S|s)et(s)?|FilterDropdown(s)?|(F|f)orm(Control|Group|Dialog)?(s)?|Pagination(s)?|RadioButton(Panel)?(s)?|RemoteTrigger(.+)Dialog(s)?|RightFixedNote(s)?|SegmentedControl(s)?|SideNav(s)?|Switch(s)?|TabItem(s)?)$'
|
|
15
15
|
const defaultInteractiveRegex = `/(${defaultInteractiveMessage})/`
|
|
16
16
|
const messageNonInteractiveEventHandler = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => {
|
|
17
17
|
const onAttrsText = onAttrs.join(', ')
|
|
@@ -50,7 +50,7 @@ ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
|
50
50
|
{ code: '<Wrapper onClick={any} role="presentation"><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }] },
|
|
51
51
|
{ code: '<Wrapper onClick={any} role="presentation"><any><Link /></any></Wrapper>' },
|
|
52
52
|
{ code: '<Wrapper onClick={any} role="presentation">{any && <AnyButton />}</Wrapper>' },
|
|
53
|
-
{ code: '<Wrapper onClick={any} role="presentation">{any || <
|
|
53
|
+
{ code: '<Wrapper onClick={any} role="presentation">{any || <AnyButtons />}</Wrapper>' },
|
|
54
54
|
{ code: '<Wrapper onClick={any} role="presentation">{any1 && (any2 || any3) && <AnyButton />}</Wrapper>' },
|
|
55
55
|
{ code: '<Wrapper onClick={any} role="presentation">{any ? <AnyButton /> : null}</Wrapper>' },
|
|
56
56
|
{ code: '<Wrapper onClick={any} role="presentation">{any1 ? (any2 ? <HogeLink /> : null) : null}</Wrapper>' },
|
|
@@ -19,10 +19,11 @@ const pageMessage = 'smarthr-ui/PageHeading が同一ファイル内に複数存
|
|
|
19
19
|
const pageInSectionMessage = 'smarthr-ui/PageHeadingはsmarthr-uiのArticle, Aside, Nav, Sectionで囲まないでください。囲んでしまうとページ全体の見出しではなくなってしまいます。'
|
|
20
20
|
const noTagAttrMessage = `tag属性を指定せず、smarthr-uiのArticle, Aside, Nav, Sectionのいずれかの自動レベル計算に任せるよう、tag属性を削除してください。
|
|
21
21
|
- tag属性を指定することで意図しないレベルに固定されてしまう可能性があります。`
|
|
22
|
-
const notHaveHeadingMessage = (elementName) => `${elementName} はHeading要素を含んでいません。
|
|
22
|
+
const notHaveHeadingMessage = (elementName, isNav) => `${elementName} はHeading要素を含んでいません。
|
|
23
23
|
- SectioningContentはHeadingを含むようにマークアップする必要があります
|
|
24
24
|
- ${elementName}に設定しているいずれかの属性がHeading,もしくはHeadingのテキストに該当する場合、その属性の名称を /^(heading|title)$/ にマッチする名称に変更してください
|
|
25
|
-
- Headingにするべき適切な文字列が存在しない場合、 ${elementName} は削除するか、SectioningContent
|
|
25
|
+
- Headingにするべき適切な文字列が存在しない場合、 ${elementName} は削除するか、SectioningContentではない要素に差し替えてください${isNav ? `
|
|
26
|
+
- nav要素の場合、aria-label、もしくはaria-labelledby属性を設定し、どんなナビゲーションなのかがわかる名称を設定してください` : ''}`
|
|
26
27
|
|
|
27
28
|
ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
28
29
|
valid: [
|
|
@@ -48,6 +49,8 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
|
48
49
|
{ code: '<HogeStack forwardedAs="section"><div><Heading>hoge</Heading></div></HogeStack>' },
|
|
49
50
|
{ code: '<HogeBase as="aside"><Heading>hoge</Heading></HogeBase>' },
|
|
50
51
|
{ code: '<HogeBaseColumn forwardedAs="nav"><Heading>hoge</Heading></HogeBaseColumn>' },
|
|
52
|
+
{ code: '<HogeNav aria-label="any"><Any /></HogeNav>' },
|
|
53
|
+
{ code: '<HogeNav aria-labelledby="any"><Any /></HogeNav>' },
|
|
51
54
|
],
|
|
52
55
|
invalid: [
|
|
53
56
|
{ code: 'const StyledArticle = styled.article``', errors: [ { message: `"article"を利用せず、smarthr-ui/Articleを拡張してください。Headingのレベルが自動計算されるようになります。(例: "styled.article" -> "styled(Article)")` } ] },
|
|
@@ -64,5 +67,7 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
|
64
67
|
{ code: '<Section></Section>', errors: [ { message: notHaveHeadingMessage('Section') } ] },
|
|
65
68
|
{ code: '<Aside><HogeSection></HogeSection></Aside>', errors: [ { message: notHaveHeadingMessage('Aside') }, { message: notHaveHeadingMessage('HogeSection') } ] },
|
|
66
69
|
{ code: '<Aside any="hoge"><HogeSection><Heading /></HogeSection></Aside>', errors: [ { message: notHaveHeadingMessage('Aside') } ] },
|
|
70
|
+
{ code: '<HogeNav><Any /></HogeNav>', errors: [ { message: notHaveHeadingMessage('HogeNav', true) } ] },
|
|
71
|
+
{ code: '<HogeNav aria-diabled="true"><Any /></HogeNav>', errors: [ { message: notHaveHeadingMessage('HogeNav', true) } ] },
|
|
67
72
|
],
|
|
68
73
|
});
|
|
@@ -55,10 +55,12 @@ ruleTester.run('best-practice-for-button-element', rule, {
|
|
|
55
55
|
{ code: `<HogeCluster justify="end" gap={0}>{a}</HogeCluster>` },
|
|
56
56
|
{ code: `<Stack align="flex-end">{a}</Stack>` },
|
|
57
57
|
{ code: `<HogeStack align="end">{a}</HogeStack>` },
|
|
58
|
+
{ code: `<Cluster>{a}</Cluster>` },
|
|
59
|
+
{ code: `<Cluster>{a.b}</Cluster>` },
|
|
60
|
+
{ code: `<Cluster>{a ? b : c}</Cluster>` },
|
|
58
61
|
],
|
|
59
62
|
invalid: [
|
|
60
63
|
{ code: `<Stack><Hoge /></Stack>`, errors: [ { message: errorMessage('Stack', 'Stack') } ] },
|
|
61
|
-
{ code: `<Stack>{a}</Stack>`, errors: [ { message: errorMessage('Stack', 'Stack') } ] },
|
|
62
64
|
{ code: `<AnyStack>{a.hoge(action)}</AnyStack>`, errors: [ { message: errorMessage('Stack', 'AnyStack') } ] },
|
|
63
65
|
{ code: `<AnyStack>{a && <><Hoge /></>}</AnyStack>`, errors: [ { message: errorMessage('Stack', 'AnyStack') } ] },
|
|
64
66
|
{ code: `<AnyStack>{a && a.hoge(action)}</AnyStack>`, errors: [ { message: errorMessage('Stack', 'AnyStack') } ] },
|
|
@@ -70,7 +72,6 @@ ruleTester.run('best-practice-for-button-element', rule, {
|
|
|
70
72
|
{ code: `<AnyStack>{a ? <Hoge /> : a.b.hoge(action)}</AnyStack>`, errors: [ { message: errorMessage('Stack', 'AnyStack') } ] },
|
|
71
73
|
{ code: `<AnyStack>{a ? <Hoge /> : a ? <Hoge /> : a.b.hoge(action)}</AnyStack>`, errors: [ { message: errorMessage('Stack', 'AnyStack') } ] },
|
|
72
74
|
{ code: `<Cluster><Hoge /></Cluster>`, errors: [ { message: errorMessage('Cluster', 'Cluster') } ] },
|
|
73
|
-
{ code: `<Cluster>{a}</Cluster>`, errors: [ { message: errorMessage('Cluster', 'Cluster') } ] },
|
|
74
75
|
{ code: `<AnyCluster>{a.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
75
76
|
{ code: `<AnyCluster>{a && <><Hoge /></>}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
76
77
|
{ code: `<AnyCluster>{a && a.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
@@ -81,8 +82,6 @@ ruleTester.run('best-practice-for-button-element', rule, {
|
|
|
81
82
|
{ code: `<AnyCluster>{a ? a.b.hoge(action) : <Hoge />}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
82
83
|
{ code: `<AnyCluster>{a ? <Hoge /> : a.b.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
83
84
|
{ code: `<AnyCluster>{a ? <Hoge /> : a ? <Hoge /> : a.b.hoge(action)}</AnyCluster>`, errors: [ { message: errorMessage('Cluster', 'AnyCluster') } ] },
|
|
84
|
-
{ code: `<HogeCluster justify="center">{a}</HogeCluster>`, errors: [ { message: 'HogeCluster は smarthr-ui/Cluster ではなく smarthr-ui/Center でマークアップしてください' } ] },
|
|
85
|
-
{ code: `<HogeStack align="center">{a}</HogeStack>`, errors: [ { message: 'HogeStack は smarthr-ui/Stack ではなく smarthr-ui/Center でマークアップしてください' } ] },
|
|
86
85
|
{ code: `<HogeStack gap={0}>{a}{b}</HogeStack>`, errors: [ { message: `HogeStack に "gap={0}" が指定されており、smarthr-ui/Stack の利用方法として誤っている可能性があります。以下の修正方法を検討してください。
|
|
87
86
|
- 方法1: 子要素を一つにまとめられないか検討してください
|
|
88
87
|
- 例: "<Stack gap={0}><p>hoge</p><p>fuga</p></Stack>" を "<p>hoge<br />fuga</p>" にするなど
|
|
@@ -23,13 +23,14 @@ ruleTester.run('format-translate-component', rule, {
|
|
|
23
23
|
{ code: '<Any data-wovn-enable="true">ほげ</Any>', options },
|
|
24
24
|
{ code: '<Translate>ほげ</Translate>', options },
|
|
25
25
|
{ code: '<Translate>ほげ<br />ふが</Translate>', options },
|
|
26
|
+
{ code: '<Translate>1<RangeSeparator />100</Translate>', options },
|
|
26
27
|
{ code: '<Translate>{any}</Translate>', options },
|
|
27
28
|
{ code: '<Translate dangerouslySetInnerHTML={{ __html: "ほげ" }} />', options },
|
|
28
29
|
],
|
|
29
30
|
invalid: [
|
|
30
31
|
{ code: '<Any data-translate="true">ほげ</Any>', options, errors: [ { message: 'data-translate 属性は使用せず、 @/any/path/Translate コンポーネントを利用してください' } ] },
|
|
31
|
-
{ code: '<Translate><Any>ほげ</Any></Translate>', options, errors: [ { message: 'Translate 内では <br /> 以外のタグは使えません' } ] },
|
|
32
|
-
{ code: '<Translate><Any /></Translate>', options, errors: [ { message: 'Translate 内では <br /> 以外のタグは使えません' } ] },
|
|
32
|
+
{ code: '<Translate><Any>ほげ</Any></Translate>', options, errors: [ { message: 'Translate 内では <br />, <RangeSeparator /> 以外のタグは使えません' } ] },
|
|
33
|
+
{ code: '<Translate><Any /></Translate>', options, errors: [ { message: 'Translate 内では <br />, <RangeSeparator /> 以外のタグは使えません' } ] },
|
|
33
34
|
{ code: '<Translate></Translate>', options, errors: [ { message: 'Translate 内には必ずテキストを設置してください' } ] },
|
|
34
35
|
]
|
|
35
36
|
})
|