@carbon/upgrade 11.23.0 → 11.24.0-rc.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/cli.js +51 -4
- package/package.json +3 -3
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.input.js +41 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.input.tsx +37 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.output.js +50 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu-nowrap.output.tsx +42 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.input.js +45 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.input.tsx +37 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.output.js +54 -0
- package/transforms/__testfixtures__/enable-v12-overflowmenu.output.tsx +42 -0
- package/transforms/__tests__/enable-v12-overflowmenu-test.js +26 -0
- package/transforms/enable-v12-overflowmenu.js +218 -0
package/cli.js
CHANGED
|
@@ -52641,6 +52641,44 @@ var upgrades = [
|
|
|
52641
52641
|
});
|
|
52642
52642
|
}
|
|
52643
52643
|
},
|
|
52644
|
+
{
|
|
52645
|
+
name: "enable-v12-overflowmenu",
|
|
52646
|
+
description: `
|
|
52647
|
+
Updates OverflowMenu components to v12 with optional FeatureFlags wrapping:
|
|
52648
|
+
1. Migrates to new API (OverflowMenuItem -> MenuItem)
|
|
52649
|
+
2. Updates props (itemText -> label, etc)
|
|
52650
|
+
3. Optional FeatureFlags wrapping (--wrapWithFeatureFlag=false to disable)
|
|
52651
|
+
|
|
52652
|
+
Example:
|
|
52653
|
+
Before: <OverflowMenu aria-label="menu"><OverflowMenuItem itemText="Option" /></OverflowMenu>
|
|
52654
|
+
After: <FeatureFlags enableV12Overflowmenu>
|
|
52655
|
+
<OverflowMenu label="menu"><MenuItem label="Option" /></OverflowMenu>
|
|
52656
|
+
</FeatureFlags>
|
|
52657
|
+
`,
|
|
52658
|
+
migrate: async (options) => {
|
|
52659
|
+
const transform = import_path2.default.join(
|
|
52660
|
+
TRANSFORM_DIR,
|
|
52661
|
+
"enable-v12-overflowmenu.js"
|
|
52662
|
+
);
|
|
52663
|
+
const paths = Array.isArray(options.paths) && options.paths.length > 0 ? options.paths : await (0, import_fast_glob2.default)(["**/*.{js,jsx,ts,tsx}"], {
|
|
52664
|
+
cwd: options.workspaceDir,
|
|
52665
|
+
ignore: [
|
|
52666
|
+
"**/es/**",
|
|
52667
|
+
"**/lib/**",
|
|
52668
|
+
"**/umd/**",
|
|
52669
|
+
"**/node_modules/**",
|
|
52670
|
+
"**/storybook-static/**"
|
|
52671
|
+
]
|
|
52672
|
+
});
|
|
52673
|
+
await run2({
|
|
52674
|
+
dry: !options.write,
|
|
52675
|
+
transform,
|
|
52676
|
+
paths,
|
|
52677
|
+
verbose: options.verbose,
|
|
52678
|
+
wrapWithFeatureFlag: options.wrapWithFeatureFlag
|
|
52679
|
+
});
|
|
52680
|
+
}
|
|
52681
|
+
},
|
|
52644
52682
|
{
|
|
52645
52683
|
name: "slug-prop-to-decorator-prop",
|
|
52646
52684
|
description: `
|
|
@@ -52929,7 +52967,7 @@ var upgrades = [
|
|
|
52929
52967
|
var package_default = {
|
|
52930
52968
|
name: "@carbon/upgrade",
|
|
52931
52969
|
description: "A tool for upgrading Carbon versions",
|
|
52932
|
-
version: "11.
|
|
52970
|
+
version: "11.24.0-rc.0",
|
|
52933
52971
|
license: "Apache-2.0",
|
|
52934
52972
|
bin: {
|
|
52935
52973
|
"carbon-upgrade": "./bin/carbon-upgrade.js"
|
|
@@ -52969,7 +53007,7 @@ var package_default = {
|
|
|
52969
53007
|
devDependencies: {
|
|
52970
53008
|
chalk: "^4.1.1",
|
|
52971
53009
|
"change-case": "^4.1.2",
|
|
52972
|
-
esbuild: "^0.
|
|
53010
|
+
esbuild: "^0.25.0",
|
|
52973
53011
|
execa: "^5.1.1",
|
|
52974
53012
|
"fast-glob": "^3.2.11",
|
|
52975
53013
|
"fs-extra": "^11.0.0",
|
|
@@ -53009,6 +53047,10 @@ async function main({ argv, cwd }) {
|
|
|
53009
53047
|
default: false,
|
|
53010
53048
|
describe: "optionally include additional logs, useful for debugging",
|
|
53011
53049
|
type: "boolean"
|
|
53050
|
+
}).option("wrapWithFeatureFlag", {
|
|
53051
|
+
default: true,
|
|
53052
|
+
describe: "wrap the migration with a feature flag",
|
|
53053
|
+
type: "boolean"
|
|
53012
53054
|
});
|
|
53013
53055
|
cli.usage("Usage: $0 [options]").command(
|
|
53014
53056
|
["upgrade", "$0"],
|
|
@@ -53040,14 +53082,19 @@ async function main({ argv, cwd }) {
|
|
|
53040
53082
|
);
|
|
53041
53083
|
},
|
|
53042
53084
|
run3(async (args) => {
|
|
53043
|
-
const { verbose, migration, write, paths } = args;
|
|
53085
|
+
const { verbose, migration, write, paths, wrapWithFeatureFlag } = args;
|
|
53044
53086
|
const options = {
|
|
53045
53087
|
cwd: cwd(),
|
|
53046
53088
|
verbose,
|
|
53047
53089
|
write,
|
|
53048
53090
|
migration,
|
|
53049
|
-
paths
|
|
53091
|
+
paths,
|
|
53092
|
+
wrapWithFeatureFlag
|
|
53050
53093
|
};
|
|
53094
|
+
console.log(
|
|
53095
|
+
"CLI options wrapWithFeatureFlag:",
|
|
53096
|
+
options.wrapWithFeatureFlag
|
|
53097
|
+
);
|
|
53051
53098
|
await migrate(options, upgrades);
|
|
53052
53099
|
})
|
|
53053
53100
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbon/upgrade",
|
|
3
3
|
"description": "A tool for upgrading Carbon versions",
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.24.0-rc.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
7
7
|
"carbon-upgrade": "./bin/carbon-upgrade.js"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"chalk": "^4.1.1",
|
|
43
43
|
"change-case": "^4.1.2",
|
|
44
|
-
"esbuild": "^0.
|
|
44
|
+
"esbuild": "^0.25.0",
|
|
45
45
|
"execa": "^5.1.1",
|
|
46
46
|
"fast-glob": "^3.2.11",
|
|
47
47
|
"fs-extra": "^11.0.0",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"@ibm/telemetry-js": "^1.5.0",
|
|
62
62
|
"jscodeshift": "^17.0.0"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "faf649817d3be3e8e258aba866e14e9378b5c68e"
|
|
65
65
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
function TestComponent({ menuProps }) {
|
|
6
|
+
return (
|
|
7
|
+
<div>
|
|
8
|
+
{/* Old API usage - with explicit props */}
|
|
9
|
+
<OverflowMenu
|
|
10
|
+
aria-label="overflow-menu"
|
|
11
|
+
align="bottom"
|
|
12
|
+
flipped={true}
|
|
13
|
+
light={true}
|
|
14
|
+
size="xl">
|
|
15
|
+
<OverflowMenuItem
|
|
16
|
+
className="test-class"
|
|
17
|
+
itemText="Stop app"
|
|
18
|
+
disabled={false}
|
|
19
|
+
onClick={() => {}}
|
|
20
|
+
/>
|
|
21
|
+
<OverflowMenuItem itemText="Restart app" />
|
|
22
|
+
<OverflowMenuItem hasDivider isDelete itemText="Delete app" />
|
|
23
|
+
</OverflowMenu>
|
|
24
|
+
|
|
25
|
+
{/* Old API with spread props */}
|
|
26
|
+
<OverflowMenu {...menuProps}>
|
|
27
|
+
<OverflowMenuItem itemText="Dynamic item" />
|
|
28
|
+
<OverflowMenuItem hasDivider isDelete itemText="Remove" />
|
|
29
|
+
</OverflowMenu>
|
|
30
|
+
|
|
31
|
+
{/* Already using new API - should not be transformed */}
|
|
32
|
+
<OverflowMenu label="Already migrated">
|
|
33
|
+
<MenuItem label="Option 1" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Delete" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default TestComponent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
5
|
+
|
|
6
|
+
interface MenuItem {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
action?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items: MenuItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
{/* Old API usage - mapped items */}
|
|
20
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
21
|
+
{items.map((item) => (
|
|
22
|
+
<OverflowMenuItem
|
|
23
|
+
key={item.id}
|
|
24
|
+
itemText={item.label}
|
|
25
|
+
onClick={item.action}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
{/* Old API - explicit props */}
|
|
30
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
31
|
+
<OverflowMenuItem hasDivider isDelete itemText="TypeScript Item" />
|
|
32
|
+
</OverflowMenu>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default TestComponent;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, MenuItem, MenuItemDivider, Button } from '@carbon/react';
|
|
4
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
5
|
+
|
|
6
|
+
function TestComponent({ menuProps }) {
|
|
7
|
+
return (
|
|
8
|
+
<div>
|
|
9
|
+
{/* Old API usage with props - transformed */}
|
|
10
|
+
<OverflowMenu
|
|
11
|
+
label="overflow-menu"
|
|
12
|
+
align="bottom"
|
|
13
|
+
flipped={true}
|
|
14
|
+
light={true}
|
|
15
|
+
size="xl"
|
|
16
|
+
>
|
|
17
|
+
<MenuItem
|
|
18
|
+
className="test-class"
|
|
19
|
+
label="Stop app"
|
|
20
|
+
disabled={false}
|
|
21
|
+
onClick={() => {}}
|
|
22
|
+
/>
|
|
23
|
+
<MenuItem label="Restart app" />
|
|
24
|
+
<MenuItemDivider />
|
|
25
|
+
<MenuItem label="Delete app" kind="danger" />
|
|
26
|
+
</OverflowMenu>
|
|
27
|
+
|
|
28
|
+
{/* Old API with spread props */}
|
|
29
|
+
<OverflowMenu {...menuProps}>
|
|
30
|
+
<MenuItem label="Dynamic item" />
|
|
31
|
+
<MenuItemDivider />
|
|
32
|
+
<MenuItem label="Remove" kind="danger" />
|
|
33
|
+
</OverflowMenu>
|
|
34
|
+
|
|
35
|
+
{/* Already using new API - should not be transformed */}
|
|
36
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
37
|
+
<OverflowMenu label="Already migrated">
|
|
38
|
+
<MenuItem label="Option 1" />
|
|
39
|
+
<MenuItemDivider />
|
|
40
|
+
<MenuItem label="Delete" kind="danger" />
|
|
41
|
+
</OverflowMenu>
|
|
42
|
+
</FeatureFlags>
|
|
43
|
+
|
|
44
|
+
{/* Other components - unchanged */}
|
|
45
|
+
<Button>Normal button</Button>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default TestComponent;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
import { MenuItem, MenuItemDivider, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
6
|
+
|
|
7
|
+
interface MenuItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
action?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
18
|
+
return (
|
|
19
|
+
(<div>
|
|
20
|
+
{/* Old API usage - mapped items */}
|
|
21
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
22
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<OverflowMenuItem
|
|
25
|
+
key={item.id}
|
|
26
|
+
itemText={item.label}
|
|
27
|
+
onClick={item.action}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
</OverflowMenu>
|
|
31
|
+
</FeatureFlags>
|
|
32
|
+
{/* Old API - explicit props */}
|
|
33
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
34
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
35
|
+
<MenuItemDivider /><MenuItem kind='danger' label="TypeScript Item" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
</div>)
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default TestComponent;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, Button } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
function TestComponent({ menuProps }) {
|
|
6
|
+
return (
|
|
7
|
+
<div>
|
|
8
|
+
{/* Old API usage - with explicit props */}
|
|
9
|
+
<OverflowMenu
|
|
10
|
+
aria-label="overflow-menu"
|
|
11
|
+
flipped={true}
|
|
12
|
+
light={true}
|
|
13
|
+
size="xl">
|
|
14
|
+
<OverflowMenuItem
|
|
15
|
+
className="test-class"
|
|
16
|
+
itemText="Stop app"
|
|
17
|
+
disabled={false}
|
|
18
|
+
onClick={() => {}}
|
|
19
|
+
/>
|
|
20
|
+
<OverflowMenuItem itemText="Restart app" />
|
|
21
|
+
<OverflowMenuItem hasDivider isDelete itemText="Delete app" />
|
|
22
|
+
</OverflowMenu>
|
|
23
|
+
|
|
24
|
+
{/* Old API with spread props */}
|
|
25
|
+
<OverflowMenu {...menuProps}>
|
|
26
|
+
<OverflowMenuItem itemText="Dynamic item" />
|
|
27
|
+
<OverflowMenuItem hasDivider isDelete itemText="Remove" />
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
|
|
30
|
+
{/* Already using new API - should not be transformed */}
|
|
31
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
32
|
+
<OverflowMenu label="Already migrated">
|
|
33
|
+
<MenuItem label="Option 1" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Delete" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
|
|
39
|
+
{/* Other components - should not be transformed */}
|
|
40
|
+
<Button>Normal button</Button>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default TestComponent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { FC } from 'react';
|
|
4
|
+
import { OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
5
|
+
|
|
6
|
+
interface MenuItem {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
action?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items: MenuItem[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
{/* Old API usage - mapped items */}
|
|
20
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
21
|
+
{items.map((item) => (
|
|
22
|
+
<OverflowMenuItem
|
|
23
|
+
key={item.id}
|
|
24
|
+
itemText={item.label}
|
|
25
|
+
onClick={item.action}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</OverflowMenu>
|
|
29
|
+
{/* Old API - explicit props */}
|
|
30
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
31
|
+
<OverflowMenuItem hasDivider isDelete itemText="TypeScript Item" />
|
|
32
|
+
</OverflowMenu>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default TestComponent;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { OverflowMenu, OverflowMenuItem, MenuItem, MenuItemDivider, Button } from '@carbon/react';
|
|
4
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
5
|
+
|
|
6
|
+
function TestComponent({ menuProps }) {
|
|
7
|
+
return (
|
|
8
|
+
(<div>
|
|
9
|
+
{/* Old API usage with props - transformed */}
|
|
10
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
11
|
+
<OverflowMenu
|
|
12
|
+
label="overflow-menu"
|
|
13
|
+
align="bottom"
|
|
14
|
+
flipped={true}
|
|
15
|
+
light={true}
|
|
16
|
+
size="xl"
|
|
17
|
+
>
|
|
18
|
+
<MenuItem
|
|
19
|
+
className="test-class"
|
|
20
|
+
label="Stop app"
|
|
21
|
+
disabled={false}
|
|
22
|
+
onClick={() => {}}
|
|
23
|
+
/>
|
|
24
|
+
<MenuItem label="Restart app" />
|
|
25
|
+
<MenuItemDivider />
|
|
26
|
+
<MenuItem label="Delete app" kind="danger" />
|
|
27
|
+
</OverflowMenu>
|
|
28
|
+
</FeatureFlags>
|
|
29
|
+
|
|
30
|
+
{/* Old API with spread props */}
|
|
31
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
32
|
+
<OverflowMenu {...menuProps}>
|
|
33
|
+
<MenuItem label="Dynamic item" />
|
|
34
|
+
<MenuItemDivider />
|
|
35
|
+
<MenuItem label="Remove" kind="danger" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
|
|
39
|
+
{/* Already using new API - should not be transformed */}
|
|
40
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
41
|
+
<OverflowMenu label="Already migrated">
|
|
42
|
+
<MenuItem label="Option 1" />
|
|
43
|
+
<MenuItemDivider />
|
|
44
|
+
<MenuItem label="Delete" kind="danger" />
|
|
45
|
+
</OverflowMenu>
|
|
46
|
+
</FeatureFlags>
|
|
47
|
+
|
|
48
|
+
{/* Other components - unchanged */}
|
|
49
|
+
<Button>Normal button</Button>
|
|
50
|
+
</div>)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default TestComponent;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//prettier-ignore
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FeatureFlags } from '@carbon/feature-flags';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
import { MenuItem, MenuItemDivider, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
6
|
+
|
|
7
|
+
interface MenuItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
action?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TestComponent: FC<Props> = ({ items }) => {
|
|
18
|
+
return (
|
|
19
|
+
(<div>
|
|
20
|
+
{/* Old API usage - mapped items */}
|
|
21
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
22
|
+
<OverflowMenu aria-label="mapped-menu">
|
|
23
|
+
{items.map((item) => (
|
|
24
|
+
<OverflowMenuItem
|
|
25
|
+
key={item.id}
|
|
26
|
+
itemText={item.label}
|
|
27
|
+
onClick={item.action}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
</OverflowMenu>
|
|
31
|
+
</FeatureFlags>
|
|
32
|
+
{/* Old API - explicit props */}
|
|
33
|
+
<FeatureFlags enableV12Overflowmenu>
|
|
34
|
+
<OverflowMenu direction="top" size="lg" flipped={true}>
|
|
35
|
+
<MenuItemDivider /><MenuItem kind='danger' label="TypeScript Item" />
|
|
36
|
+
</OverflowMenu>
|
|
37
|
+
</FeatureFlags>
|
|
38
|
+
</div>)
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default TestComponent;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const { defineTest } = require('jscodeshift/dist/testUtils');
|
|
11
|
+
|
|
12
|
+
// Test with wrapping (default)
|
|
13
|
+
defineTest(
|
|
14
|
+
__dirname,
|
|
15
|
+
'enable-v12-overflowmenu',
|
|
16
|
+
{ wrapWithFeatureFlag: 'true' },
|
|
17
|
+
'enable-v12-overflowmenu'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Test without wrapping
|
|
21
|
+
defineTest(
|
|
22
|
+
__dirname,
|
|
23
|
+
'enable-v12-overflowmenu',
|
|
24
|
+
{ wrapWithFeatureFlag: 'false' },
|
|
25
|
+
'enable-v12-overflowmenu-nowrap'
|
|
26
|
+
);
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright IBM Corp. 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* Migrate OverflowMenu components to v12 API
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const defaultOptions = {
|
|
13
|
+
quote: 'single',
|
|
14
|
+
trailingComma: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Props mapping from OverflowMenuItem to MenuItem
|
|
18
|
+
const MENU_ITEM_PROPS_MAP = {
|
|
19
|
+
itemText: 'label',
|
|
20
|
+
href: 'href',
|
|
21
|
+
disabled: 'disabled',
|
|
22
|
+
className: 'className',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const EVENT_HANDLERS = new Set(['onClick']);
|
|
26
|
+
|
|
27
|
+
function transform(fileInfo, api, options) {
|
|
28
|
+
const { jscodeshift: j } = api;
|
|
29
|
+
const root = j(fileInfo.source);
|
|
30
|
+
const printOptions = options.printOptions || defaultOptions;
|
|
31
|
+
const shouldWrapWithFlags = options.wrapWithFeatureFlag !== false;
|
|
32
|
+
const overflowMenuElements = root
|
|
33
|
+
.find(j.JSXElement, {
|
|
34
|
+
openingElement: { name: { name: 'OverflowMenu' } },
|
|
35
|
+
})
|
|
36
|
+
.filter((path) => {
|
|
37
|
+
let parent = path.parent;
|
|
38
|
+
while (parent) {
|
|
39
|
+
if (
|
|
40
|
+
parent.node.type === 'JSXElement' &&
|
|
41
|
+
parent.node.openingElement.name.name === 'FeatureFlags'
|
|
42
|
+
) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
parent = parent.parent;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!overflowMenuElements.length) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add required imports
|
|
55
|
+
const importsToAdd = ['MenuItem', 'MenuItemDivider'].sort();
|
|
56
|
+
const carbonImport = root.find(j.ImportDeclaration, {
|
|
57
|
+
source: { value: '@carbon/react' },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (carbonImport.length) {
|
|
61
|
+
const importNode = carbonImport.get(0);
|
|
62
|
+
const existingSpecifiers = new Set(
|
|
63
|
+
importNode.node.specifiers
|
|
64
|
+
.filter((spec) => spec.type === 'ImportSpecifier')
|
|
65
|
+
.map((spec) => spec.imported.name)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
importsToAdd.forEach((importName) => {
|
|
69
|
+
if (!existingSpecifiers.has(importName)) {
|
|
70
|
+
importNode.node.specifiers.push(
|
|
71
|
+
j.importSpecifier(j.identifier(importName))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Sort specifiers alphabetically
|
|
77
|
+
importNode.node.specifiers.sort((a, b) =>
|
|
78
|
+
a.imported.name.localeCompare(b.imported.name)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function transformOverflowMenuItems(elements) {
|
|
83
|
+
elements.forEach((path) => {
|
|
84
|
+
path.node.children.forEach((child, index) => {
|
|
85
|
+
if (
|
|
86
|
+
child.type === 'JSXElement' &&
|
|
87
|
+
child.openingElement.name.name === 'OverflowMenuItem'
|
|
88
|
+
) {
|
|
89
|
+
const itemProps = [];
|
|
90
|
+
let needsDivider = false;
|
|
91
|
+
let classNames = [];
|
|
92
|
+
|
|
93
|
+
child.openingElement.attributes.forEach((attr) => {
|
|
94
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
95
|
+
itemProps.push(attr);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const propName = attr.name.name;
|
|
100
|
+
|
|
101
|
+
if (MENU_ITEM_PROPS_MAP[propName]) {
|
|
102
|
+
if (propName === 'className') {
|
|
103
|
+
classNames.push(attr.value.value);
|
|
104
|
+
} else {
|
|
105
|
+
itemProps.push(
|
|
106
|
+
j.jsxAttribute(
|
|
107
|
+
j.jsxIdentifier(MENU_ITEM_PROPS_MAP[propName]),
|
|
108
|
+
attr.value
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
} else if (propName === 'wrapperClassName') {
|
|
113
|
+
classNames.push(attr.value.value);
|
|
114
|
+
} else if (propName === 'hasDivider') {
|
|
115
|
+
needsDivider = true;
|
|
116
|
+
} else if (propName === 'isDelete') {
|
|
117
|
+
itemProps.push(
|
|
118
|
+
j.jsxAttribute(
|
|
119
|
+
j.jsxIdentifier('kind'),
|
|
120
|
+
j.stringLiteral('danger')
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} else if (EVENT_HANDLERS.has(propName)) {
|
|
124
|
+
itemProps.push(attr);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (classNames.length > 0) {
|
|
129
|
+
itemProps.push(
|
|
130
|
+
j.jsxAttribute(
|
|
131
|
+
j.jsxIdentifier('className'),
|
|
132
|
+
j.stringLiteral(classNames.join(' '))
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (needsDivider) {
|
|
138
|
+
path.node.children.splice(
|
|
139
|
+
index,
|
|
140
|
+
0,
|
|
141
|
+
j.jsxElement(
|
|
142
|
+
j.jsxOpeningElement(
|
|
143
|
+
j.jsxIdentifier('MenuItemDivider'),
|
|
144
|
+
[],
|
|
145
|
+
true
|
|
146
|
+
),
|
|
147
|
+
null,
|
|
148
|
+
[],
|
|
149
|
+
true
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
child.openingElement.name = j.jsxIdentifier('MenuItem');
|
|
155
|
+
child.openingElement.attributes = itemProps;
|
|
156
|
+
if (child.closingElement) {
|
|
157
|
+
child.closingElement.name = j.jsxIdentifier('MenuItem');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function addFeatureFlagsImport() {
|
|
165
|
+
const hasFeatureFlagsImport = root
|
|
166
|
+
.find(j.ImportDeclaration)
|
|
167
|
+
.some(
|
|
168
|
+
(path) =>
|
|
169
|
+
path.node.source.value === '@carbon/feature-flags' &&
|
|
170
|
+
path.node.specifiers.some(
|
|
171
|
+
(spec) => spec.imported && spec.imported.name === 'FeatureFlags'
|
|
172
|
+
)
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (!hasFeatureFlagsImport) {
|
|
176
|
+
const featureFlagsImport = j.importDeclaration(
|
|
177
|
+
[j.importSpecifier(j.identifier('FeatureFlags'))],
|
|
178
|
+
j.literal('@carbon/feature-flags')
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const firstImport = root.find(j.ImportDeclaration).at(0);
|
|
182
|
+
if (firstImport.length) {
|
|
183
|
+
firstImport.insertAfter(featureFlagsImport);
|
|
184
|
+
} else {
|
|
185
|
+
root.get().node.program.body.unshift(featureFlagsImport);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function wrapWithFeatureFlags(elements) {
|
|
191
|
+
elements.forEach((path) => {
|
|
192
|
+
const wrappedElement = j.jsxElement(
|
|
193
|
+
j.jsxOpeningElement(
|
|
194
|
+
j.jsxIdentifier('FeatureFlags'),
|
|
195
|
+
[j.jsxAttribute(j.jsxIdentifier('enableV12Overflowmenu'))],
|
|
196
|
+
false
|
|
197
|
+
),
|
|
198
|
+
j.jsxClosingElement(j.jsxIdentifier('FeatureFlags')),
|
|
199
|
+
[j.jsxText('\n '), path.node, j.jsxText('\n ')]
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
j(path).replaceWith(wrappedElement);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Transform based on wrap option
|
|
207
|
+
transformOverflowMenuItems(overflowMenuElements);
|
|
208
|
+
|
|
209
|
+
if (shouldWrapWithFlags) {
|
|
210
|
+
addFeatureFlagsImport();
|
|
211
|
+
wrapWithFeatureFlags(overflowMenuElements);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return root.toSource(printOptions);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = transform;
|
|
218
|
+
module.exports.parser = 'tsx';
|