eslint-plugin-smarthr 0.3.20 → 0.3.21
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 +13 -0
- package/libs/format_styled_components.js +10 -7
- package/package.json +1 -1
- package/rules/a11y-delegate-element-has-role-presentation/index.js +78 -10
- package/rules/a11y-heading-in-sectioning-content/index.js +15 -1
- package/test/a11y-delegate-element-has-role-presantation.js +3 -2
- package/test/a11y-heading-in-sectioning-content.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
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
|
+
### [0.3.21](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.20...v0.3.21) (2024-01-16)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* a11y-heading-in-sectioning-content の Layout[as={sectioningContentTag}] への対応 ([#91](https://github.com/kufu/eslint-plugin-smarthr/issues/91)) ([62bb051](https://github.com/kufu/eslint-plugin-smarthr/commit/62bb0510d0e91db063a9bd09dab4fbeff6a2844e))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* a11y-delegate-element-has-role-presentationでインタラクティブな要素のチェック処理を厳密に行う ([#100](https://github.com/kufu/eslint-plugin-smarthr/issues/100)) ([e715bff](https://github.com/kufu/eslint-plugin-smarthr/commit/e715bff6a95ab2cbf3652b44f999ad4c98ccb621))
|
|
16
|
+
* import時のrenameの際、typeの場合はチェックを無視する設定を追加 ([#99](https://github.com/kufu/eslint-plugin-smarthr/issues/99)) ([cae899f](https://github.com/kufu/eslint-plugin-smarthr/commit/cae899ff2b012e93b2651c971034553cefeef10e))
|
|
17
|
+
|
|
5
18
|
### [0.3.20](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.19...v0.3.20) (2024-01-02)
|
|
6
19
|
|
|
7
20
|
### [0.3.19](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.3.18...v0.3.19) (2024-01-01)
|
|
@@ -27,12 +27,13 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
27
27
|
}) : []
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
const checkImportedNameToLocalName = (node, base, extended) => {
|
|
30
|
+
const checkImportedNameToLocalName = (node, base, extended, mode) => {
|
|
31
31
|
entriesesTagNames.forEach(([b, e]) => {
|
|
32
32
|
if (base.match(b) && !extended.match(e)) {
|
|
33
33
|
context.report({
|
|
34
34
|
node,
|
|
35
|
-
message: `${extended}を正規表現 "${e.toString()}" がmatch
|
|
35
|
+
message: `${extended}を正規表現 "${e.toString()}" がmatchする名称に変更してください。
|
|
36
|
+
- ${base}が型の場合、'${mode} type { ${base} as ${extended} }' もしくは '${mode} { type ${base} as ${extended} }' のように明示的に型であることを宣言してください。名称変更が不要になります`,
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
})
|
|
@@ -42,11 +43,13 @@ const generateTagFormatter = ({ context, EXPECTED_NAMES, UNEXPECTED_NAMES }) =>
|
|
|
42
43
|
ImportDeclaration: (node) => {
|
|
43
44
|
checkImportStyledComponents(node, context)
|
|
44
45
|
|
|
45
|
-
node.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
if (node.importKind !== 'type') {
|
|
47
|
+
node.specifiers.forEach((s) => {
|
|
48
|
+
if (s.importKind !== 'type' && s.imported && s.imported.name !== s.local.name) {
|
|
49
|
+
checkImportedNameToLocalName(node, s.imported.name, s.local.name, 'import')
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
50
53
|
},
|
|
51
54
|
VariableDeclarator: (node) => {
|
|
52
55
|
if (!node.init) {
|
package/package.json
CHANGED
|
@@ -14,6 +14,9 @@ const EXPECTED_NAMES = {
|
|
|
14
14
|
'SegmentedControl$': 'SegmentedControl$',
|
|
15
15
|
'RightFixedNote$': 'RightFixedNote$',
|
|
16
16
|
'FieldSet$': 'FieldSet$',
|
|
17
|
+
'Fieldset$': 'Fieldset$',
|
|
18
|
+
'FormControl$': 'FormControl$',
|
|
19
|
+
'FormGroup$': 'FormGroup$',
|
|
17
20
|
'(b|B)utton$': 'Button$',
|
|
18
21
|
'Anchor$': 'Anchor$',
|
|
19
22
|
'Link$': 'Link$',
|
|
@@ -56,7 +59,7 @@ const messageNonInteractiveEventHandler = (nodeName, interactiveComponentRegex,
|
|
|
56
59
|
- 'role="presentation"' を設定した要素はマークアップとしての意味がなくなるため、div・span などマークアップとしての意味を持たない要素に設定してください
|
|
57
60
|
- 'role="presentation"' を設定する適切な要素が存在しない場合、div、またはspanでイベントが発生する要素を囲んだ上でrole属性を設定してください`
|
|
58
61
|
}
|
|
59
|
-
const messageRolePresentationNotHasInteractive = (nodeName, interactiveComponentRegex, onAttrs) => `${nodeName}に 'role="
|
|
62
|
+
const messageRolePresentationNotHasInteractive = (nodeName, interactiveComponentRegex, onAttrs, roleMean) => `${nodeName}に 'role="${roleMean}"' が設定されているにも関わらず、子要素にinput、buttonやaなどのインタラクティブな要素が見つからないため、ブラウザが正しく解釈が行えず、ユーザーが利用することが出来ない場合があるため、以下のいずれかの対応をおこなってください。
|
|
60
63
|
- 方法1: 子要素にインタラクティブな要素が存在するにも関わらずこのエラーが表示されている場合、子要素の名称を変更してください
|
|
61
64
|
- "${interactiveComponentRegex}" の正規表現にmatchするよう、インタラクティブな子要素全てを差し替える、もしくは名称を変更してください
|
|
62
65
|
- 方法2: ${nodeName}自体がインタラクティブな要素の場合、'role="presentation"'を削除した上で名称を変更してください
|
|
@@ -117,7 +120,7 @@ module.exports = {
|
|
|
117
120
|
const nodeName = node.name.name || '';
|
|
118
121
|
|
|
119
122
|
let onAttrs = []
|
|
120
|
-
let
|
|
123
|
+
let roleMean = undefined
|
|
121
124
|
let isRolePresentation = false
|
|
122
125
|
|
|
123
126
|
node.attributes.forEach((a) => {
|
|
@@ -129,9 +132,10 @@ module.exports = {
|
|
|
129
132
|
const v = a.value?.value || ''
|
|
130
133
|
|
|
131
134
|
if (v === 'presentation') {
|
|
132
|
-
|
|
135
|
+
isRolePresentation = true
|
|
136
|
+
roleMean = v
|
|
133
137
|
} else if (v.match(MEANED_ROLE_REGEX)) {
|
|
134
|
-
|
|
138
|
+
roleMean = v
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
})
|
|
@@ -147,16 +151,80 @@ module.exports = {
|
|
|
147
151
|
// HINT: role="presentation"以外で意味があるroleが設定されている場合はエラーにしない
|
|
148
152
|
// 基本的にsmarthr-uiでroleの設定などは巻き取る && そもそもroleを設定するよりタグを適切にマークアップすることが優先されるため
|
|
149
153
|
// エラーなどには表示しない
|
|
150
|
-
if (!
|
|
154
|
+
if (!roleMean) {
|
|
151
155
|
context.report({
|
|
152
156
|
node,
|
|
153
157
|
message: messageNonInteractiveEventHandler(nodeName, interactiveComponentRegex, onAttrs),
|
|
154
158
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
// HINT: role='slider' はインタラクティブな要素扱いとするため除外する
|
|
160
|
+
} else if (roleMean !== 'slider') {
|
|
161
|
+
const searchChildren = (n) => {
|
|
162
|
+
switch (n.type) {
|
|
163
|
+
case 'BinaryExpression':
|
|
164
|
+
case 'Identifier':
|
|
165
|
+
case 'JSXEmptyExpression':
|
|
166
|
+
case 'JSXText':
|
|
167
|
+
case 'Literal':
|
|
168
|
+
case 'VariableDeclaration':
|
|
169
|
+
// これ以上childrenが存在しないため終了
|
|
170
|
+
return false
|
|
171
|
+
case 'JSXAttribute':
|
|
172
|
+
return n.value ? searchChildren(n.value) : false
|
|
173
|
+
case 'LogicalExpression':
|
|
174
|
+
return searchChildren(n.right)
|
|
175
|
+
case 'ArrowFunctionExpression':
|
|
176
|
+
return searchChildren(n.body)
|
|
177
|
+
case 'MemberExpression':
|
|
178
|
+
return searchChildren(n.property)
|
|
179
|
+
case 'ReturnStatement':
|
|
180
|
+
case 'UnaryExpression':
|
|
181
|
+
return searchChildren(n.argument)
|
|
182
|
+
case 'ChainExpression':
|
|
183
|
+
case 'JSXExpressionContainer':
|
|
184
|
+
return searchChildren(n.expression)
|
|
185
|
+
case 'BlockStatement':
|
|
186
|
+
return forInSearchChildren(n.body)
|
|
187
|
+
case 'ConditionalExpression':
|
|
188
|
+
return searchChildren(n.consequent) || searchChildren(n.alternate)
|
|
189
|
+
case 'CallExpression': {
|
|
190
|
+
return forInSearchChildren(n.arguments)
|
|
191
|
+
}
|
|
192
|
+
case 'JSXFragment':
|
|
193
|
+
break
|
|
194
|
+
case 'JSXElement': {
|
|
195
|
+
const name = n.openingElement.name.name || ''
|
|
196
|
+
|
|
197
|
+
if (name.match(interactiveComponentRegex)) {
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (forInSearchChildren(n.openingElement.attributes)) {
|
|
202
|
+
return true
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return n.children ? forInSearchChildren(n.children) : false
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const forInSearchChildren = (ary) => {
|
|
213
|
+
for (const i in ary) {
|
|
214
|
+
if (searchChildren(ary[i])) {
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!forInSearchChildren(node.parent.children)) {
|
|
223
|
+
context.report({
|
|
224
|
+
node,
|
|
225
|
+
message: messageRolePresentationNotHasInteractive(nodeName, interactiveComponentRegex, onAttrs, roleMean)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
160
228
|
}
|
|
161
229
|
}
|
|
162
230
|
},
|
|
@@ -10,6 +10,10 @@ const EXPECTED_NAMES = {
|
|
|
10
10
|
'Nav$': 'Nav$',
|
|
11
11
|
'Section$': 'Section$',
|
|
12
12
|
'ModelessDialog$': 'ModelessDialog$',
|
|
13
|
+
'Center$': 'Center$',
|
|
14
|
+
'Reel$': 'Reel$',
|
|
15
|
+
'Sidebar$': 'Sidebar$',
|
|
16
|
+
'Stack$': 'Stack$',
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
const unexpectedMessageTemplate = `{{extended}} は smarthr-ui/{{expected}} をextendすることを期待する名称になっています
|
|
@@ -34,6 +38,10 @@ const UNEXPECTED_NAMES = {
|
|
|
34
38
|
'(Section)$',
|
|
35
39
|
unexpectedMessageTemplate,
|
|
36
40
|
],
|
|
41
|
+
'Center$': '(Center)$',
|
|
42
|
+
'Reel$': '(Reel)$',
|
|
43
|
+
'Sidebar$': '(Sidebar)$',
|
|
44
|
+
'Stack$': '(Stack)$',
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
const headingRegex = /((^h(1|2|3|4|5|6))|Heading)$/
|
|
@@ -42,6 +50,9 @@ const declaratorHeadingRegex = /Heading$/
|
|
|
42
50
|
const sectioningRegex = /((A(rticle|side))|Nav|Section|^SectioningFragment)$/
|
|
43
51
|
const bareTagRegex = /^(article|aside|nav|section)$/
|
|
44
52
|
const modelessDialogRegex = /ModelessDialog$/
|
|
53
|
+
const layoutComponentRegex = /((C(ent|lust)er)|Reel|Sidebar|Stack)$/
|
|
54
|
+
|
|
55
|
+
const includeSectioningAsAttr = ({name: { name }, value: { value }}) => name === 'as' && value.match(bareTagRegex)
|
|
45
56
|
|
|
46
57
|
const noHeadingTagNames = ['span', 'legend']
|
|
47
58
|
const ignoreHeadingCheckParentType = ['Program', 'ExportNamedDeclaration']
|
|
@@ -89,7 +100,10 @@ const reportMessageBareToSHR = (tagName, visibleExample) => {
|
|
|
89
100
|
const searchBubbleUp = (node) => {
|
|
90
101
|
if (
|
|
91
102
|
node.type === 'Program' ||
|
|
92
|
-
node.type === 'JSXElement' && node.openingElement.name.name
|
|
103
|
+
node.type === 'JSXElement' && node.openingElement.name.name && (
|
|
104
|
+
node.openingElement.name.name.match(sectioningRegex) ||
|
|
105
|
+
node.openingElement.name.name.match(layoutComponentRegex) && node.openingElement.attributes.some(includeSectioningAsAttr)
|
|
106
|
+
)
|
|
93
107
|
) {
|
|
94
108
|
return node
|
|
95
109
|
}
|
|
@@ -12,7 +12,7 @@ const ruleTester = new RuleTester({
|
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
const defaultInteractiveRegex = '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$)/'
|
|
15
|
+
const defaultInteractiveRegex = '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|Fieldset$|FormControl$|FormGroup$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$)/'
|
|
16
16
|
const messageNonInteractiveEventHandler = (nodeName, onAttrs, interactiveComponentRegex = defaultInteractiveRegex) => {
|
|
17
17
|
const onAttrsText = onAttrs.join(', ')
|
|
18
18
|
|
|
@@ -55,6 +55,7 @@ ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
|
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>' },
|
|
57
57
|
{ code: '<Wrapper onClick={any} role="presentation">{any ? null : (hoge ? <AnyLink /> : null)}</Wrapper>' },
|
|
58
|
+
{ code: '<Wrapper onClick={any} role="slider">Hoge</Wrapper>' },
|
|
58
59
|
],
|
|
59
60
|
invalid: [
|
|
60
61
|
{ code: '<Input role="presentation" />', errors: [ { message: messageInteractiveHasRolePresentation('Input') } ] },
|
|
@@ -64,7 +65,7 @@ ruleTester.run('smarthr/a11y-delegate-element-has-role-presentation', rule, {
|
|
|
64
65
|
{ code: '<div onClick={any} onSubmit={any2} role="presentation"><Hoge /></div>', errors: [ { message: messageRolePresentationNotHasInteractive('div', ['onClick', 'onSubmit']) } ] },
|
|
65
66
|
{ code: '<div onClick={any}><Link /></div>', errors: [ { message: messageNonInteractiveEventHandler('div', ['onClick']) } ] },
|
|
66
67
|
{ code: '<Wrapper onClick={any}><Link /></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick']) } ] },
|
|
67
|
-
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onSubmit'], '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/') } ] },
|
|
68
|
+
{ code: '<Wrapper onSubmit={any}><Hoge /></Wrapper>', options: [{ additionalInteractiveComponentRegex: ['^Hoge$'] }], errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onSubmit'], '/((i|I)nput$|(t|T)extarea$|(s|S)elect$|InputFile$|RadioButtonPanel$|Check(b|B)ox$|Combo(b|B)ox$|DatePicker$|DropZone$|Switch$|SegmentedControl$|RightFixedNote$|FieldSet$|Fieldset$|FormControl$|FormGroup$|(b|B)utton$|Anchor$|Link$|TabItem$|^a$|(f|F)orm$|ActionDialogWithTrigger$|RemoteDialogTrigger$|RemoteTrigger(.+)Dialog$|FormDialog$|Pagination$|SideNav$|AccordionPanel$|^Hoge$)/') } ] },
|
|
68
69
|
{ code: '<Wrapper onClick={any} onChange={anyany}><any><Link /></any></Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick', 'onChange']) } ] },
|
|
69
70
|
{ code: '<Wrapper onClick={any}>{any ? null : (hoge ? <AnyLink /> : null)}</Wrapper>', errors: [ { message: messageNonInteractiveEventHandler('Wrapper', ['onClick']) } ] },
|
|
70
71
|
],
|
|
@@ -46,11 +46,20 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
|
46
46
|
{ code: 'const FugaNav = styled(HogeNav)``' },
|
|
47
47
|
{ code: 'const FugaSection = styled(HogeSection)``' },
|
|
48
48
|
{ code: "const FugaHeading = styled(Heading).attrs(() => ({ type: 'blockTitle' }))``" },
|
|
49
|
+
{ code: 'const FugaCenter = styled(HogeCenter)``' },
|
|
50
|
+
{ code: 'const FugaReel = styled(HogeReel)``' },
|
|
51
|
+
{ code: 'const FugaSidebar = styled(HogeSidebar)``' },
|
|
52
|
+
{ code: 'const FugaStack = styled(HogeStack)``' },
|
|
49
53
|
{ code: '<PageHeading>hoge</PageHeading>' },
|
|
50
54
|
{ code: '<Section><Heading>hoge</Heading></Section>' },
|
|
51
55
|
{ code: '<><Section><Heading>hoge</Heading></Section><Section><Heading>fuga</Heading></Section></>' },
|
|
52
56
|
{ code: 'const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>' },
|
|
53
57
|
{ code: 'export const HogeHeading = () => <FugaHeading anyArg={abc}>hoge</FugaHeading>' },
|
|
58
|
+
{ code: '<Center as="section"><div><Heading>hoge</Heading></div></Center>' },
|
|
59
|
+
{ code: '<Cluster as="section"><div><Heading>hoge</Heading></div></Cluster>' },
|
|
60
|
+
{ code: '<Reel as="aside"><div><Heading>hoge</Heading></div></Reel>' },
|
|
61
|
+
{ code: '<Sidebar as="nav"><div><Heading>hoge</Heading></div></Sidebar>' },
|
|
62
|
+
{ code: '<Stack as="section"><div><Heading>hoge</Heading></div></Stack>' },
|
|
54
63
|
],
|
|
55
64
|
invalid: [
|
|
56
65
|
{ code: `import hoge from 'styled-components'`, errors: [ { message: `styled-components をimportする際は、名称が"styled" となるようにしてください。例: "import styled from 'styled-components'"` } ] },
|
|
@@ -74,6 +83,10 @@ ruleTester.run('a11y-heading-in-sectioning-content', rule, {
|
|
|
74
83
|
{ code: 'const Fuga = styled(HogeAside)``', errors: [ { message: `Fugaを正規表現 "/Aside$/" がmatchする名称に変更してください` } ] },
|
|
75
84
|
{ code: 'const Fuga = styled(HogeNav)``', errors: [ { message: `Fugaを正規表現 "/Nav$/" がmatchする名称に変更してください` } ] },
|
|
76
85
|
{ code: 'const Fuga = styled(HogeSection)``', errors: [ { message: `Fugaを正規表現 "/Section$/" がmatchする名称に変更してください` } ] },
|
|
86
|
+
{ code: 'const Fuga = styled(HogeCenter)``', errors: [ { message: `Fugaを正規表現 "/Center$/" がmatchする名称に変更してください` } ] },
|
|
87
|
+
{ code: 'const Fuga = styled(HogeReel)``', errors: [ { message: `Fugaを正規表現 "/Reel$/" がmatchする名称に変更してください` } ] },
|
|
88
|
+
{ code: 'const Fuga = styled(HogeSidebar)``', errors: [ { message: `Fugaを正規表現 "/Sidebar$/" がmatchする名称に変更してください` } ] },
|
|
89
|
+
{ code: 'const Fuga = styled(HogeStack)``', errors: [ { message: `Fugaを正規表現 "/Stack$/" がmatchする名称に変更してください` } ] },
|
|
77
90
|
{ code: 'const StyledArticle = styled.article``', errors: [ { message: `"article"を利用せず、smarthr-ui/Articleを拡張してください。Headingのレベルが自動計算されるようになります。(例: "styled.article" -> "styled(Article)")` } ] },
|
|
78
91
|
{ code: 'const StyledAside = styled.aside``', errors: [ { message: `"aside"を利用せず、smarthr-ui/Asideを拡張してください。Headingのレベルが自動計算されるようになります。(例: "styled.aside" -> "styled(Aside)")` } ] },
|
|
79
92
|
{ code: 'const StyledNav = styled.nav``', errors: [ { message: `"nav"を利用せず、smarthr-ui/Navを拡張してください。Headingのレベルが自動計算されるようになります。(例: "styled.nav" -> "styled(Nav)")` } ] },
|