Skip to content

Commit 2d59305

Browse files
authored
feat: codemode to enable-v12-structured-list-visible-icons (#18615)
* feat: codemode for v12-structured-list * feat: adds messageConfig in migration * chore: format * chore: cleanup * refactor: support for both arrow and regular function
1 parent 448a2b7 commit 2d59305

8 files changed

+1365
-0
lines changed

packages/upgrade/src/commands/migrate.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ async function runMigration(migration, workspaces, options) {
8585
);
8686

8787
await migration.migrate({ ...options, workspaceDir: workspace.directory });
88+
// Display post run message if migration provides messageConfig
89+
if (migration.messageConfig && !options.dry) {
90+
logger.log('\n');
91+
logger.log(`🎉 ${migration.messageConfig.name} Migration Complete!`);
92+
logger.log('-'.repeat(migration.messageConfig.name.length + 22));
93+
94+
migration.messageConfig.changes?.forEach((change) => {
95+
logger.log(change);
96+
});
97+
98+
migration.messageConfig.nextSteps?.forEach((step) => {
99+
logger.log(step);
100+
});
101+
}
88102
}
89103

90104
/**

packages/upgrade/src/upgrades.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,79 @@ export const upgrades = [
664664
});
665665
},
666666
},
667+
{
668+
name: 'enable-v12-structured-list-visible-icons',
669+
description: `
670+
Updates selectable StructuredList components with new v12 selection pattern.
671+
https://react.carbondesignsystem.com/?path=/story/components-structuredlist-feature-flag--selection
672+
673+
Key Changes:
674+
• Replaces checkmark icons with radio buttons
675+
• Moves selection indicators to first column (previously last column)
676+
• Makes radio buttons always visible (not just on hover)
677+
• Identifies StructuredListWrapper components with the selection prop
678+
• Automatically adds selection prop to all child StructuredListRow components
679+
680+
681+
Transforms:
682+
Before migration:
683+
<StructuredListWrapper selection>
684+
<StructuredListRow>
685+
<StructuredListCell>Content</StructuredListCell>
686+
<StructuredListCell><CheckmarkFilled /></StructuredListCell>
687+
</StructuredListRow>
688+
</StructuredListWrapper>
689+
690+
After migration:
691+
<StructuredListWrapper selection>
692+
<StructuredListRow selection>
693+
<StructuredListCell>Content</StructuredListCell>
694+
</StructuredListRow>
695+
</StructuredListWrapper>
696+
`,
697+
messageConfig: {
698+
name: 'StructuredList',
699+
nextSteps: [
700+
'⚠️ IMPORTANT: Additional SASS Changes Required!',
701+
'This migration requires enabling "enable-v12-structured-list-visible-icons" SASS feature flag in your stylesheet.',
702+
'$enable-v12-structured-list-visible-icons: true',
703+
'📚 For detailed instructions on enabling SASS feature flags, visit:',
704+
'https://react.carbondesignsystem.com/iframe.html?args=size%3Amini&viewMode=docs&id=getting-started-feature-flags--overview&globals=#turning-on-feature-flags-in-sass',
705+
'',
706+
],
707+
},
708+
migrate: async (options) => {
709+
const transform = path.join(
710+
TRANSFORM_DIR,
711+
'enable-v12-structured-list-visible-icons.js'
712+
);
713+
const paths =
714+
Array.isArray(options.paths) && options.paths.length > 0
715+
? options.paths
716+
: await glob(['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], {
717+
cwd: options.workspaceDir,
718+
ignore: [
719+
'**/es/**',
720+
'**/lib/**',
721+
'**/umd/**',
722+
'**/node_modules/**',
723+
'**/storybook-static/**',
724+
'**/dist/**',
725+
'**/build/**',
726+
'**/*.d.ts',
727+
'**/coverage/**',
728+
],
729+
});
730+
731+
await run({
732+
dry: !options.write,
733+
transform,
734+
paths,
735+
verbose: options.verbose,
736+
parser: 'tsx',
737+
});
738+
},
739+
},
667740
],
668741
},
669742
{
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
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+
import React from 'react';
9+
import {
10+
StructuredListWrapper,
11+
StructuredListHead,
12+
StructuredListBody,
13+
StructuredListRow,
14+
StructuredListCell,
15+
StructuredListInput,
16+
} from '@carbon/react';
17+
18+
// Case 1: Direct usage with selection
19+
export const BasicSelection = () => (
20+
<StructuredListWrapper selection>
21+
<StructuredListHead>
22+
<StructuredListRow head selection>
23+
<StructuredListCell head>Column A</StructuredListCell>
24+
<StructuredListCell head>Column B</StructuredListCell>
25+
</StructuredListRow>
26+
</StructuredListHead>
27+
<StructuredListBody>
28+
<StructuredListRow selection>
29+
<StructuredListCell>Row 1</StructuredListCell>
30+
<StructuredListCell>Row 1</StructuredListCell>
31+
<StructuredListInput
32+
id="row-1"
33+
value="row-1"
34+
title="row-1"
35+
name="row-1"
36+
aria-label="row-1"
37+
/>
38+
</StructuredListRow>
39+
</StructuredListBody>
40+
</StructuredListWrapper>
41+
);
42+
43+
// Case 2: Row generator function
44+
const generateRows = (count) => {
45+
return Array.from({ length: count }, (_, i) => (
46+
<StructuredListRow key={`row-${i}`} selection>
47+
<StructuredListCell>Row {i}</StructuredListCell>
48+
<StructuredListCell>Content {i}</StructuredListCell>
49+
<StructuredListInput
50+
id={`row-${i}`}
51+
value={`row-${i}`}
52+
title={`row-${i}`}
53+
name="row-0"
54+
aria-label={`row-${i}`}
55+
/>
56+
</StructuredListRow>
57+
));
58+
};
59+
60+
export const WithRowGenerator = () => (
61+
<StructuredListWrapper selection>
62+
<StructuredListHead>
63+
<StructuredListRow head selection>
64+
<StructuredListCell head>Column A</StructuredListCell>
65+
<StructuredListCell head>Column B</StructuredListCell>
66+
</StructuredListRow>
67+
</StructuredListHead>
68+
<StructuredListBody>{generateRows(3)}</StructuredListBody>
69+
</StructuredListWrapper>
70+
);
71+
72+
// Case 3: Nested inside other components
73+
export const NestedStructuredList = () => (
74+
<div className="wrapper">
75+
<div className="inner">
76+
<StructuredListWrapper selection>
77+
<StructuredListHead>
78+
<StructuredListRow head selection>
79+
<StructuredListCell head>Nested Column</StructuredListCell>
80+
</StructuredListRow>
81+
</StructuredListHead>
82+
<StructuredListBody>
83+
<StructuredListRow selection>
84+
<StructuredListCell>Nested Content</StructuredListCell>
85+
<StructuredListInput
86+
id="nested-1"
87+
value="nested-1"
88+
title="nested-1"
89+
name="nested-1"
90+
aria-label="nested-1"
91+
/>
92+
</StructuredListRow>
93+
</StructuredListBody>
94+
</StructuredListWrapper>
95+
</div>
96+
</div>
97+
);
98+
99+
// Case 4: Without selection (should not be modified)
100+
export const WithoutSelection = () => (
101+
<StructuredListWrapper>
102+
<StructuredListHead>
103+
<StructuredListRow head>
104+
<StructuredListCell head>No Selection</StructuredListCell>
105+
</StructuredListRow>
106+
</StructuredListHead>
107+
<StructuredListBody>
108+
<StructuredListRow>
109+
<StructuredListCell>Should Not Change</StructuredListCell>
110+
</StructuredListRow>
111+
</StructuredListBody>
112+
</StructuredListWrapper>
113+
);
114+
115+
// Case 5: Mixed with and without selection
116+
export const MixedSelectionUsage = () => (
117+
<div>
118+
<StructuredListWrapper selection>
119+
<StructuredListHead>
120+
<StructuredListRow head selection>
121+
<StructuredListCell head>With Selection</StructuredListCell>
122+
</StructuredListRow>
123+
</StructuredListHead>
124+
<StructuredListBody>
125+
<StructuredListRow selection>
126+
<StructuredListCell>Should Get Selection</StructuredListCell>
127+
<StructuredListInput
128+
id="mixed-1"
129+
value="mixed-1"
130+
title="mixed-1"
131+
name="mixed-1"
132+
aria-label="mixed-1"
133+
/>
134+
</StructuredListRow>
135+
</StructuredListBody>
136+
</StructuredListWrapper>
137+
<StructuredListWrapper>
138+
<StructuredListHead>
139+
<StructuredListRow head>
140+
<StructuredListCell head>Without Selection</StructuredListCell>
141+
</StructuredListRow>
142+
</StructuredListHead>
143+
<StructuredListBody>
144+
<StructuredListRow>
145+
<StructuredListCell>Should Not Get Selection</StructuredListCell>
146+
</StructuredListRow>
147+
</StructuredListBody>
148+
</StructuredListWrapper>
149+
</div>
150+
);
151+
// Case 7: Conditional Rendering with Selection
152+
export const ConditionalRendering = ({ showSelection = true }) => (
153+
<StructuredListWrapper selection={showSelection}>
154+
<StructuredListHead>
155+
<StructuredListRow head selection>
156+
<StructuredListCell head>Conditional</StructuredListCell>
157+
</StructuredListRow>
158+
</StructuredListHead>
159+
<StructuredListBody>
160+
<StructuredListRow selection>
161+
<StructuredListCell>Conditional Row</StructuredListCell>
162+
<StructuredListInput
163+
id="conditional-1"
164+
value="conditional-1"
165+
title="conditional-1"
166+
name="conditional"
167+
aria-label="conditional-1"
168+
/>
169+
</StructuredListRow>
170+
</StructuredListBody>
171+
</StructuredListWrapper>
172+
);
173+
174+
// Case 8: Multiple Nested StructuredLists
175+
export const NestedLists = () => (
176+
<StructuredListWrapper selection>
177+
<StructuredListHead>
178+
<StructuredListRow head selection>
179+
<StructuredListCell head>Parent List</StructuredListCell>
180+
</StructuredListRow>
181+
</StructuredListHead>
182+
<StructuredListBody>
183+
<StructuredListRow selection>
184+
<StructuredListCell>
185+
<StructuredListWrapper selection>
186+
<StructuredListHead>
187+
<StructuredListRow head selection>
188+
<StructuredListCell head>Nested List</StructuredListCell>
189+
</StructuredListRow>
190+
</StructuredListHead>
191+
<StructuredListBody>
192+
<StructuredListRow selection>
193+
<StructuredListCell>Nested Content</StructuredListCell>
194+
<StructuredListInput
195+
id="nested-2"
196+
value="nested-2"
197+
title="nested-2"
198+
name="nested-2"
199+
aria-label="nested-2"
200+
/>
201+
</StructuredListRow>
202+
</StructuredListBody>
203+
</StructuredListWrapper>
204+
</StructuredListCell>
205+
<StructuredListInput
206+
id="parent-1"
207+
value="parent-1"
208+
title="parent-1"
209+
name="parent"
210+
aria-label="parent-1"
211+
/>
212+
</StructuredListRow>
213+
</StructuredListBody>
214+
</StructuredListWrapper>
215+
);
216+
217+
// Case 10: With Conditional StructuredListInput
218+
export const ConditionalInput = ({ isSelectable = true }) => (
219+
<StructuredListWrapper selection>
220+
<StructuredListHead>
221+
<StructuredListRow head selection>
222+
<StructuredListCell head>Conditional Input</StructuredListCell>
223+
</StructuredListRow>
224+
</StructuredListHead>
225+
<StructuredListBody>
226+
<StructuredListRow selection>
227+
<StructuredListCell>Row Content</StructuredListCell>
228+
{isSelectable && (
229+
<StructuredListInput
230+
id="conditional-input-1"
231+
value="conditional-input-1"
232+
title="conditional-input-1"
233+
name="conditional-input"
234+
aria-label="conditional-input-1"
235+
/>
236+
)}
237+
</StructuredListRow>
238+
</StructuredListBody>
239+
</StructuredListWrapper>
240+
);
241+
242+
// Case 11: With Multiple Body Sections
243+
export const MultipleBodySections = () => (
244+
<StructuredListWrapper selection>
245+
<StructuredListHead>
246+
<StructuredListRow head selection>
247+
<StructuredListCell head>Multiple Bodies</StructuredListCell>
248+
</StructuredListRow>
249+
</StructuredListHead>
250+
<StructuredListBody>
251+
<StructuredListRow selection>
252+
<StructuredListCell>Section 1</StructuredListCell>
253+
<StructuredListInput
254+
id="section-1"
255+
value="section-1"
256+
title="section-1"
257+
name="multi-section"
258+
aria-label="section-1"
259+
/>
260+
</StructuredListRow>
261+
</StructuredListBody>
262+
<StructuredListBody>
263+
<StructuredListRow selection>
264+
<StructuredListCell>Section 2</StructuredListCell>
265+
<StructuredListInput
266+
id="section-2"
267+
value="section-2"
268+
title="section-2"
269+
name="multi-section"
270+
aria-label="section-2"
271+
/>
272+
</StructuredListRow>
273+
</StructuredListBody>
274+
</StructuredListWrapper>
275+
);

0 commit comments

Comments
 (0)