@kaizen/components 1.74.3 → 1.75.0
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/bin/codemod.sh +1 -0
- package/codemods/README.md +6 -0
- package/codemods/migrateGuidanceBlockActionsToActionsSlot/index.ts +21 -0
- package/codemods/migrateGuidanceBlockActionsToActionsSlot/migrateGuidanceBlockActionsToActionsSlot.spec.ts +122 -0
- package/codemods/migrateGuidanceBlockActionsToActionsSlot/migrateGuidanceBlockActionsToActionsSlot.ts +102 -0
- package/codemods/migrateGuidanceBlockActionsToActionsSlot/transformActionsToActionsSlot.spec.ts +196 -0
- package/codemods/migrateGuidanceBlockActionsToActionsSlot/transformActionsToActionsSlot.ts +71 -0
- package/codemods/utils/createProp.ts +1 -1
- package/codemods/utils/index.ts +1 -0
- package/codemods/utils/transformV1ButtonPropsToButtonOrLinkButton.spec.tsx +199 -0
- package/codemods/utils/transformV1ButtonPropsToButtonOrLinkButton.ts +195 -0
- package/dist/cjs/GuidanceBlock/GuidanceBlock.cjs +49 -84
- package/dist/cjs/GuidanceBlock/GuidanceBlock.module.css.cjs +0 -2
- package/dist/esm/GuidanceBlock/GuidanceBlock.mjs +50 -84
- package/dist/esm/GuidanceBlock/GuidanceBlock.module.css.mjs +0 -2
- package/dist/styles.css +1117 -1126
- package/dist/types/GuidanceBlock/GuidanceBlock.d.ts +8 -4
- package/package.json +1 -2
- package/src/GuidanceBlock/GuidanceBlock.module.css +0 -9
- package/src/GuidanceBlock/GuidanceBlock.tsx +48 -87
- package/src/GuidanceBlock/_docs/GuidanceBlock--migration-guide.mdx +77 -0
- package/src/GuidanceBlock/_docs/GuidanceBlock.mdx +35 -6
- package/src/GuidanceBlock/_docs/GuidanceBlock.stickersheet.stories.tsx +60 -27
- package/src/GuidanceBlock/_docs/GuidanceBlock.stories.tsx +205 -4
- package/src/Notification/InlineNotification/_docs/InlineNotification.stories.tsx +1 -0
- package/src/__next__/Button/_docs/Button--migration-guide.mdx +3 -3
- package/src/__next__/Tag/Tag/_docs/Tag.stories.tsx +10 -0
package/bin/codemod.sh
CHANGED
|
@@ -35,6 +35,7 @@ if npx tsx@latest $CODEMOD_PATH $TARGET_DIR; then
|
|
|
35
35
|
echo "Run linting and prettier to correct issues with re-writes"
|
|
36
36
|
else
|
|
37
37
|
echo "Codemod '$codemodFileName' could not be run in '$TARGET_DIR'"
|
|
38
|
+
echo "Check if '$CODEMOD_PATH' exists and is in the current version of the @kaizen/components"
|
|
38
39
|
exit 1
|
|
39
40
|
fi
|
|
40
41
|
|
package/codemods/README.md
CHANGED
|
@@ -109,6 +109,12 @@ Released in `1.73.1`
|
|
|
109
109
|
|
|
110
110
|
Migrates V1 `Button` and `IconButton` component to next `Button` or `LinkButton`.
|
|
111
111
|
|
|
112
|
+
### `migrateGuidanceBlockActionsToActionsSlot`
|
|
113
|
+
|
|
114
|
+
Migrates GuidanceBlock `actions` props into `actionsSlot` with `Button` or `LinkButton` component.
|
|
115
|
+
|
|
116
|
+
Released in `1.74.0`
|
|
117
|
+
|
|
112
118
|
#### Props
|
|
113
119
|
|
|
114
120
|
- `label` becomes `children`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { transformComponentsInDir } from '../utils'
|
|
2
|
+
import { migrateGuidanceBlockActionsToActionsSlot } from './migrateGuidanceBlockActionsToActionsSlot'
|
|
3
|
+
|
|
4
|
+
const run = (): void => {
|
|
5
|
+
console.log('~(-_- ~) Running migrateGuidanceBlockActionsToActionsSlot upgrade (~ -_-)~')
|
|
6
|
+
|
|
7
|
+
const targetDir = process.argv[2]
|
|
8
|
+
if (!targetDir) {
|
|
9
|
+
process.exit(1)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
transformComponentsInDir(targetDir, ['GuidanceBlock'], (tagNames) => [
|
|
13
|
+
migrateGuidanceBlockActionsToActionsSlot(tagNames),
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
console.log(
|
|
17
|
+
'---\nIt is recommended that the `upgradeIconV1` codemod if any v1 icons have been migrated',
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
run()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { parseJsx } from '../__tests__/utils'
|
|
2
|
+
import {
|
|
3
|
+
getKaioTagNamesMapByComponentName,
|
|
4
|
+
printAst,
|
|
5
|
+
transformSource,
|
|
6
|
+
type TransformSourceArgs,
|
|
7
|
+
} from '../utils'
|
|
8
|
+
import { migrateGuidanceBlockActionsToActionsSlot } from './migrateGuidanceBlockActionsToActionsSlot'
|
|
9
|
+
|
|
10
|
+
const transformGuidanceBlock = (sourceFile: TransformSourceArgs['sourceFile']): string => {
|
|
11
|
+
const kaioTagNamesMap = getKaioTagNamesMapByComponentName(sourceFile, ['GuidanceBlock'])
|
|
12
|
+
return transformSource({
|
|
13
|
+
sourceFile,
|
|
14
|
+
transformers: [migrateGuidanceBlockActionsToActionsSlot(kaioTagNamesMap!)],
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('transformActionsToButtonNext()', () => {
|
|
19
|
+
it('imports Button from the /next path and transforms all button-like actions prop into a Button component', () => {
|
|
20
|
+
const inputAst = parseJsx(`
|
|
21
|
+
import { GuidanceBlock } from "@kaizen/components"
|
|
22
|
+
<GuidanceBlock
|
|
23
|
+
layout="default"
|
|
24
|
+
illustration={<Informative alt="" />}
|
|
25
|
+
content={<div>Test</div>}
|
|
26
|
+
actions={{
|
|
27
|
+
primary: {
|
|
28
|
+
label: 'Primary action',
|
|
29
|
+
onClick: () => alert('click 1'),
|
|
30
|
+
},
|
|
31
|
+
secondary: {
|
|
32
|
+
label: 'Secondary action',
|
|
33
|
+
onClick: () => alert('click 2'),
|
|
34
|
+
},
|
|
35
|
+
}}
|
|
36
|
+
/>`)
|
|
37
|
+
const outputAst = parseJsx(`
|
|
38
|
+
import { GuidanceBlock } from "@kaizen/components"
|
|
39
|
+
import { Button } from "@kaizen/components/next"
|
|
40
|
+
<GuidanceBlock
|
|
41
|
+
layout="default"
|
|
42
|
+
illustration={<Informative alt="" />}
|
|
43
|
+
content={<div>Test</div>}
|
|
44
|
+
actionsSlot={<><Button onPress={() => alert('click 1')} variant="secondary" size="large">Primary action</Button><Button onPress={() => alert('click 2')} variant="tertiary" size="large">Secondary action</Button></>}
|
|
45
|
+
/>
|
|
46
|
+
`)
|
|
47
|
+
|
|
48
|
+
expect(transformGuidanceBlock(inputAst)).toBe(printAst(outputAst))
|
|
49
|
+
})
|
|
50
|
+
it('imports and transforms all button-like and link-like actions prop into a Button and LinkButton', () => {
|
|
51
|
+
const inputAst = parseJsx(`
|
|
52
|
+
import { GuidanceBlock } from "@kaizen/components"
|
|
53
|
+
<GuidanceBlock
|
|
54
|
+
layout="default"
|
|
55
|
+
illustration={<Informative alt="" />}
|
|
56
|
+
content={<div>Test</div>}
|
|
57
|
+
actions={{
|
|
58
|
+
primary: {
|
|
59
|
+
label: 'Primary action',
|
|
60
|
+
onClick: () => alert('click 1'),
|
|
61
|
+
},
|
|
62
|
+
secondary: {
|
|
63
|
+
label: 'Secondary action',
|
|
64
|
+
href: "#secondary"
|
|
65
|
+
},
|
|
66
|
+
}}
|
|
67
|
+
/>`)
|
|
68
|
+
const outputAst = parseJsx(`
|
|
69
|
+
import { GuidanceBlock, LinkButton } from "@kaizen/components"
|
|
70
|
+
import { Button } from "@kaizen/components/next"
|
|
71
|
+
<GuidanceBlock
|
|
72
|
+
layout="default"
|
|
73
|
+
illustration={<Informative alt="" />}
|
|
74
|
+
content={<div>Test</div>}
|
|
75
|
+
actionsSlot={<><Button onPress={() => alert('click 1')} variant="secondary" size="large">Primary action</Button><LinkButton href="#secondary" variant="tertiary" size="large">Secondary action</LinkButton></>}
|
|
76
|
+
/>
|
|
77
|
+
`)
|
|
78
|
+
|
|
79
|
+
expect(transformGuidanceBlock(inputAst)).toBe(printAst(outputAst))
|
|
80
|
+
})
|
|
81
|
+
it('does not redeclare imports if found', () => {
|
|
82
|
+
const inputAst = parseJsx(`
|
|
83
|
+
import { GuidanceBlock } from "@kaizen/components"
|
|
84
|
+
import { Button } from "@kaizen/components/next"
|
|
85
|
+
const MockLayout = () => (
|
|
86
|
+
<>
|
|
87
|
+
<GuidanceBlock
|
|
88
|
+
layout="default"
|
|
89
|
+
illustration={<Informative alt="" />}
|
|
90
|
+
content={<div>Test</div>}
|
|
91
|
+
actions={{
|
|
92
|
+
primary: {
|
|
93
|
+
label: 'Primary action',
|
|
94
|
+
onClick: () => alert('click 1'),
|
|
95
|
+
},
|
|
96
|
+
secondary: {
|
|
97
|
+
label: 'Secondary action',
|
|
98
|
+
href: "#secondary"
|
|
99
|
+
},
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
<Button onPress={() => alert('page click 1')} variant="primary" size="large">Page button</Button>
|
|
103
|
+
</>
|
|
104
|
+
)`)
|
|
105
|
+
const outputAst = parseJsx(`
|
|
106
|
+
import { GuidanceBlock, LinkButton } from "@kaizen/components"
|
|
107
|
+
import { Button } from "@kaizen/components/next"
|
|
108
|
+
const MockLayout = () => (
|
|
109
|
+
<>
|
|
110
|
+
<GuidanceBlock
|
|
111
|
+
layout="default"
|
|
112
|
+
illustration={<Informative alt="" />}
|
|
113
|
+
content={<div>Test</div>}
|
|
114
|
+
actionsSlot={<><Button onPress={() => alert('click 1')} variant="secondary" size="large">Primary action</Button><LinkButton href="#secondary" variant="tertiary" size="large">Secondary action</LinkButton></>}
|
|
115
|
+
/>
|
|
116
|
+
<Button onPress={() => alert('page click 1')} variant="primary" size="large">Page button</Button>
|
|
117
|
+
</>
|
|
118
|
+
)`)
|
|
119
|
+
|
|
120
|
+
expect(transformGuidanceBlock(inputAst)).toBe(printAst(outputAst))
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import {
|
|
3
|
+
getKaioTagName,
|
|
4
|
+
setImportToAdd,
|
|
5
|
+
updateKaioImports,
|
|
6
|
+
type TagImportAttributesMap,
|
|
7
|
+
type UpdateKaioImportsArgs,
|
|
8
|
+
} from '../utils'
|
|
9
|
+
import { transformActionsToActionsSlot } from './transformActionsToActionsSlot'
|
|
10
|
+
|
|
11
|
+
const BUTTON_IMPORT_DESTINATION = '@kaizen/components/next'
|
|
12
|
+
const LINKBUTTON_IMPORT_DESTINATION = '@kaizen/components'
|
|
13
|
+
|
|
14
|
+
export const migrateGuidanceBlockActionsToActionsSlot =
|
|
15
|
+
(tagsMap: TagImportAttributesMap): ts.TransformerFactory<ts.SourceFile> =>
|
|
16
|
+
(context) =>
|
|
17
|
+
(rootNode) => {
|
|
18
|
+
const importsToRemove: UpdateKaioImportsArgs['importsToRemove'] = new Map()
|
|
19
|
+
const importsToAdd: UpdateKaioImportsArgs['importsToAdd'] = new Map()
|
|
20
|
+
|
|
21
|
+
const importedTargetButtonTagName = getKaioTagName(
|
|
22
|
+
rootNode,
|
|
23
|
+
'Button',
|
|
24
|
+
BUTTON_IMPORT_DESTINATION,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const importedTargetLinkButtonTagName = getKaioTagName(
|
|
28
|
+
rootNode,
|
|
29
|
+
'LinkButton',
|
|
30
|
+
LINKBUTTON_IMPORT_DESTINATION,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const visit = (node: ts.Node): ts.Node => {
|
|
34
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
35
|
+
const tagName = node.tagName.getText()
|
|
36
|
+
const tagImportAttributes = tagsMap.get(tagName)
|
|
37
|
+
|
|
38
|
+
if (!tagImportAttributes) return node
|
|
39
|
+
if (
|
|
40
|
+
tagName === 'GuidanceBlock' ||
|
|
41
|
+
tagImportAttributes.importModuleName === 'GuidanceBlock'
|
|
42
|
+
) {
|
|
43
|
+
const componentProps: ts.JsxAttributeLike[] = node.attributes.properties.reduce<
|
|
44
|
+
ts.JsxAttributeLike[]
|
|
45
|
+
>((guidanceBlockProps, prop) => {
|
|
46
|
+
if (ts.isJsxAttribute(prop)) {
|
|
47
|
+
const propName = prop.name.getText()
|
|
48
|
+
const propValue = prop.initializer as ts.JsxExpression
|
|
49
|
+
|
|
50
|
+
if (propName === 'actions') {
|
|
51
|
+
const transformedActions = transformActionsToActionsSlot(
|
|
52
|
+
propValue.getChildren()[1] as ts.ObjectLiteralExpression,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if (transformedActions?.importsToAdd) {
|
|
56
|
+
transformedActions.importsToAdd.forEach((importToAdd) => {
|
|
57
|
+
if (importToAdd === 'Button') {
|
|
58
|
+
setImportToAdd(importsToAdd, BUTTON_IMPORT_DESTINATION, {
|
|
59
|
+
componentName: 'Button',
|
|
60
|
+
alias:
|
|
61
|
+
importedTargetButtonTagName !== 'Button'
|
|
62
|
+
? importedTargetButtonTagName
|
|
63
|
+
: undefined,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
if (importToAdd === 'LinkButton') {
|
|
67
|
+
setImportToAdd(importsToAdd, LINKBUTTON_IMPORT_DESTINATION, {
|
|
68
|
+
componentName: 'LinkButton',
|
|
69
|
+
alias:
|
|
70
|
+
importedTargetLinkButtonTagName !== 'LinkButton'
|
|
71
|
+
? importedTargetLinkButtonTagName
|
|
72
|
+
: undefined,
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return transformedActions?.actionsSlotAttr
|
|
79
|
+
? [...guidanceBlockProps, transformedActions.actionsSlotAttr]
|
|
80
|
+
: guidanceBlockProps
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [...guidanceBlockProps, prop]
|
|
84
|
+
}
|
|
85
|
+
return [...guidanceBlockProps, prop]
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
return ts.factory.createJsxSelfClosingElement(
|
|
89
|
+
ts.factory.createIdentifier(tagName),
|
|
90
|
+
undefined,
|
|
91
|
+
ts.factory.createJsxAttributes(componentProps),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
return ts.visitEachChild(node, visit, context)
|
|
95
|
+
}
|
|
96
|
+
return ts.visitEachChild(node, visit, context)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const node = ts.visitNode(rootNode, visit)
|
|
100
|
+
|
|
101
|
+
return updateKaioImports({ importsToRemove, importsToAdd })(context)(node as ts.SourceFile)
|
|
102
|
+
}
|
package/codemods/migrateGuidanceBlockActionsToActionsSlot/transformActionsToActionsSlot.spec.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import { parseJsx } from '../__tests__/utils'
|
|
3
|
+
import { printAst } from '../utils'
|
|
4
|
+
import { transformActionsToActionsSlot } from './transformActionsToActionsSlot'
|
|
5
|
+
|
|
6
|
+
export const mockedTransformer =
|
|
7
|
+
(kaioComponentName: string) =>
|
|
8
|
+
(context: ts.TransformationContext) =>
|
|
9
|
+
(rootNode: ts.Node): ts.Node => {
|
|
10
|
+
const visit = (node: ts.Node): ts.Node => {
|
|
11
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
12
|
+
const componentProps: ts.JsxAttributeLike[] = node.attributes.properties.reduce<
|
|
13
|
+
ts.JsxAttributeLike[]
|
|
14
|
+
>((guidanceBlockProps, prop) => {
|
|
15
|
+
if (ts.isJsxAttribute(prop)) {
|
|
16
|
+
const propName = prop.name.getText()
|
|
17
|
+
const propValue = prop.initializer as ts.JsxExpression
|
|
18
|
+
|
|
19
|
+
if (propName === 'actions') {
|
|
20
|
+
const transformedActions = transformActionsToActionsSlot(
|
|
21
|
+
propValue.getChildren()[1] as ts.ObjectLiteralExpression,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
return transformedActions?.actionsSlotAttr
|
|
25
|
+
? [...guidanceBlockProps, transformedActions.actionsSlotAttr]
|
|
26
|
+
: guidanceBlockProps
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [...guidanceBlockProps, prop]
|
|
30
|
+
}
|
|
31
|
+
return [...guidanceBlockProps, prop]
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
return ts.factory.createJsxSelfClosingElement(
|
|
35
|
+
ts.factory.createIdentifier(kaioComponentName),
|
|
36
|
+
undefined,
|
|
37
|
+
ts.factory.createJsxAttributes(componentProps),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
return ts.visitEachChild(node, visit, context)
|
|
41
|
+
}
|
|
42
|
+
return ts.visitNode(rootNode, visit)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const transformInput = (
|
|
46
|
+
sourceFile: ts.SourceFile,
|
|
47
|
+
kaioComponentName: string = 'GuidanceBlock',
|
|
48
|
+
): string => {
|
|
49
|
+
const result = ts.transform(sourceFile, [mockedTransformer(kaioComponentName)])
|
|
50
|
+
const transformedSource = result.transformed[0] as ts.SourceFile
|
|
51
|
+
return printAst(transformedSource)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('transformActionsToActionsSlot()', () => {
|
|
55
|
+
it('transforms button-like and link-like actions prop into a Button and LinkButton', () => {
|
|
56
|
+
const inputAst = parseJsx(`
|
|
57
|
+
<GuidanceBlock
|
|
58
|
+
layout="default"
|
|
59
|
+
illustration={<Informative alt="" />}
|
|
60
|
+
content={<div>Test</div>}
|
|
61
|
+
actions={{
|
|
62
|
+
primary: {
|
|
63
|
+
label: 'Primary action',
|
|
64
|
+
onClick: () => alert('click 1'),
|
|
65
|
+
},
|
|
66
|
+
secondary: {
|
|
67
|
+
label: 'Secondary action',
|
|
68
|
+
href: "#secondary"
|
|
69
|
+
},
|
|
70
|
+
}}
|
|
71
|
+
/>`)
|
|
72
|
+
const outputAst = parseJsx(`
|
|
73
|
+
<GuidanceBlock
|
|
74
|
+
layout="default"
|
|
75
|
+
illustration={<Informative alt="" />}
|
|
76
|
+
content={<div>Test</div>}
|
|
77
|
+
actionsSlot={<><Button onPress={() => alert('click 1')} variant="secondary" size="large">Primary action</Button><LinkButton href="#secondary" variant="tertiary" size="large">Secondary action</LinkButton></>}
|
|
78
|
+
/>
|
|
79
|
+
`)
|
|
80
|
+
|
|
81
|
+
expect(transformInput(inputAst)).toBe(printAst(outputAst))
|
|
82
|
+
})
|
|
83
|
+
it('transforms a primary action with all v1 Button props to expected Button outputs', () => {
|
|
84
|
+
const inputAst = parseJsx(`
|
|
85
|
+
<GuidanceBlock
|
|
86
|
+
layout="default"
|
|
87
|
+
illustration={<Informative alt="" />}
|
|
88
|
+
content={<div>Test</div>}
|
|
89
|
+
actions={{
|
|
90
|
+
primary: {
|
|
91
|
+
label: 'Learn more',
|
|
92
|
+
onClick: () => alert('tada: 🎉'),
|
|
93
|
+
tooltip: {
|
|
94
|
+
text: 'Opens in a new tab',
|
|
95
|
+
mood: 'cautionary',
|
|
96
|
+
},
|
|
97
|
+
badge: {
|
|
98
|
+
text: 'New',
|
|
99
|
+
},
|
|
100
|
+
destructive: true,
|
|
101
|
+
disabled: hasCondition ? true : false,
|
|
102
|
+
reversed: true,
|
|
103
|
+
icon: <Icon name="arrow_forward" shouldMirrorInRTL isPresentational />,
|
|
104
|
+
iconPosition: 'end',
|
|
105
|
+
size: 'small',
|
|
106
|
+
working: true,
|
|
107
|
+
workingLabel: 'Loading...',
|
|
108
|
+
workingLabelHidden: true,
|
|
109
|
+
disableTabFocusAndIUnderstandTheAccessibilityImplications: true,
|
|
110
|
+
'data-custom-attr': 'custom-attr',
|
|
111
|
+
},
|
|
112
|
+
}}
|
|
113
|
+
/>`)
|
|
114
|
+
const outputAst = parseJsx(`
|
|
115
|
+
<GuidanceBlock
|
|
116
|
+
layout="default"
|
|
117
|
+
illustration={<Informative alt=""/>}
|
|
118
|
+
content={<div>Test</div>}
|
|
119
|
+
actionsSlot={<><Button
|
|
120
|
+
onPress={() => alert('tada: 🎉')}
|
|
121
|
+
tooltip={{text: 'Opens in a new tab',mood: 'cautionary',}}
|
|
122
|
+
badge={{ text: 'New', }}
|
|
123
|
+
isDisabled={hasCondition ? true : false}
|
|
124
|
+
isReversed
|
|
125
|
+
icon={<Icon name="arrow_forward" shouldMirrorInRTL isPresentational/>}
|
|
126
|
+
iconPosition='end'
|
|
127
|
+
size="medium"
|
|
128
|
+
isPending pendingLabel='Loading...'
|
|
129
|
+
hasHiddenPendingLabel
|
|
130
|
+
data-custom-attr='custom-attr'
|
|
131
|
+
variant="secondary"
|
|
132
|
+
>Learn more</Button></>}/>
|
|
133
|
+
`)
|
|
134
|
+
|
|
135
|
+
expect(transformInput(inputAst).replace(/\s+/g, ' ')).toBe(
|
|
136
|
+
printAst(outputAst).replace(/\s+/g, ' '),
|
|
137
|
+
)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('Passes custom data attributes and old props to be caught by type errors', () => {
|
|
141
|
+
const inputAst = parseJsx(`
|
|
142
|
+
<GuidanceBlock
|
|
143
|
+
layout="default"
|
|
144
|
+
illustration={<Informative alt="" />}
|
|
145
|
+
content={<div>Test</div>}
|
|
146
|
+
actions={{
|
|
147
|
+
primary: {
|
|
148
|
+
label: 'Learn more',
|
|
149
|
+
onClick: () => alert('tada: 🎉'),
|
|
150
|
+
tooltip: {
|
|
151
|
+
text: 'Opens in a new tab',
|
|
152
|
+
mood: 'cautionary',
|
|
153
|
+
},
|
|
154
|
+
badge: {
|
|
155
|
+
text: 'New',
|
|
156
|
+
},
|
|
157
|
+
destructive: true,
|
|
158
|
+
disabled: hasCondition ? true : false,
|
|
159
|
+
reversed: true,
|
|
160
|
+
icon: <Icon name="arrow_forward" shouldMirrorInRTL isPresentational />,
|
|
161
|
+
iconPosition: 'end',
|
|
162
|
+
size: 'small',
|
|
163
|
+
working: true,
|
|
164
|
+
workingLabel: 'Loading...',
|
|
165
|
+
workingLabelHidden: true,
|
|
166
|
+
disableTabFocusAndIUnderstandTheAccessibilityImplications: true,
|
|
167
|
+
'data-custom-attr': 'custom-attr',
|
|
168
|
+
},
|
|
169
|
+
}}
|
|
170
|
+
/>`)
|
|
171
|
+
const outputAst = parseJsx(`
|
|
172
|
+
<GuidanceBlock
|
|
173
|
+
layout="default"
|
|
174
|
+
illustration={<Informative alt=""/>}
|
|
175
|
+
content={<div>Test</div>}
|
|
176
|
+
actionsSlot={<><Button
|
|
177
|
+
onPress={() => alert('tada: 🎉')}
|
|
178
|
+
tooltip={{text: 'Opens in a new tab',mood: 'cautionary',}}
|
|
179
|
+
badge={{ text: 'New', }}
|
|
180
|
+
isDisabled={hasCondition ? true : false}
|
|
181
|
+
isReversed
|
|
182
|
+
icon={<Icon name="arrow_forward" shouldMirrorInRTL isPresentational/>}
|
|
183
|
+
iconPosition='end'
|
|
184
|
+
size="medium"
|
|
185
|
+
isPending pendingLabel='Loading...'
|
|
186
|
+
hasHiddenPendingLabel
|
|
187
|
+
data-custom-attr='custom-attr'
|
|
188
|
+
variant="secondary"
|
|
189
|
+
>Learn more</Button></>}/>
|
|
190
|
+
`)
|
|
191
|
+
|
|
192
|
+
expect(transformInput(inputAst).replace(/\s+/g, ' ')).toBe(
|
|
193
|
+
printAst(outputAst).replace(/\s+/g, ' '),
|
|
194
|
+
)
|
|
195
|
+
})
|
|
196
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import { transformV1ButtonPropsToButtonOrLinkButton } from '../utils'
|
|
3
|
+
|
|
4
|
+
type GuidanceBlockTransformedActions = {
|
|
5
|
+
importsToAdd: string[]
|
|
6
|
+
actionsSlotAttr: ts.JsxAttributeLike
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A function that transforms a GuidanceBlock v1 button object literal into an new actionsSlot prop as JSX elements
|
|
11
|
+
* expects that the node passed in will be an ObjectLiteralExpression, ie:
|
|
12
|
+
* `{primary: {...buttonV1Props}, secondary: {...buttonV1Props}}`
|
|
13
|
+
*/
|
|
14
|
+
export const transformActionsToActionsSlot = (
|
|
15
|
+
node: ts.ObjectLiteralExpression,
|
|
16
|
+
): GuidanceBlockTransformedActions | undefined => {
|
|
17
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
18
|
+
const newImports: (string | undefined)[] = []
|
|
19
|
+
|
|
20
|
+
const primaryAction = node.properties.find((prop) => prop?.name?.getText() === 'primary') as
|
|
21
|
+
| ts.PropertyAssignment
|
|
22
|
+
| undefined
|
|
23
|
+
|
|
24
|
+
const secondaryAction = node.properties.find(
|
|
25
|
+
(prop) => prop?.name?.getText() === 'secondary',
|
|
26
|
+
) as ts.PropertyAssignment | undefined
|
|
27
|
+
|
|
28
|
+
let primaryButton: ts.JsxSelfClosingElement | ts.JsxElement | undefined = undefined
|
|
29
|
+
let secondaryButton: ts.JsxSelfClosingElement | ts.JsxElement | undefined = undefined
|
|
30
|
+
|
|
31
|
+
if (primaryAction) {
|
|
32
|
+
const actionValue = primaryAction.initializer as ts.ObjectLiteralExpression
|
|
33
|
+
const transformedComponent = transformV1ButtonPropsToButtonOrLinkButton(
|
|
34
|
+
actionValue,
|
|
35
|
+
'secondary',
|
|
36
|
+
)
|
|
37
|
+
primaryButton = transformedComponent.component
|
|
38
|
+
newImports.push(transformedComponent.import)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (secondaryAction) {
|
|
42
|
+
const actionValue = secondaryAction?.initializer as ts.ObjectLiteralExpression
|
|
43
|
+
const transformedComponent = transformV1ButtonPropsToButtonOrLinkButton(
|
|
44
|
+
actionValue,
|
|
45
|
+
'tertiary',
|
|
46
|
+
)
|
|
47
|
+
secondaryButton = transformedComponent.component
|
|
48
|
+
newImports.push(transformedComponent.import)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (primaryButton || secondaryButton) {
|
|
52
|
+
const newActionsSlotAttr = ts.factory.createJsxAttribute(
|
|
53
|
+
ts.factory.createIdentifier('actionsSlot'),
|
|
54
|
+
ts.factory.createJsxExpression(
|
|
55
|
+
undefined,
|
|
56
|
+
ts.factory.createJsxFragment(
|
|
57
|
+
ts.factory.createJsxOpeningFragment(),
|
|
58
|
+
[primaryButton, secondaryButton].filter(Boolean) as ts.JsxChild[],
|
|
59
|
+
ts.factory.createJsxJsxClosingFragment(),
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
actionsSlotAttr: newActionsSlotAttr,
|
|
66
|
+
importsToAdd: newImports.filter(Boolean) as string[],
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ts from 'typescript'
|
|
2
2
|
|
|
3
3
|
export const createProp = (
|
|
4
|
+
// Transforms `propName={true}` to `propName`
|
|
4
5
|
name: string,
|
|
5
6
|
value?: ts.JsxAttributeValue | undefined,
|
|
6
7
|
): ts.JsxAttribute => {
|
|
7
|
-
// Transforms `propName={true}` to `propName`
|
|
8
8
|
if (value && ts.isJsxExpression(value) && value.expression?.kind === ts.SyntaxKind.TrueKeyword) {
|
|
9
9
|
return ts.factory.createJsxAttribute(ts.factory.createIdentifier(name), undefined)
|
|
10
10
|
}
|
package/codemods/utils/index.ts
CHANGED