@times-design-system/components-wordpress 1.3.0 ā 1.5.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/BLOCK_CREATION_CHECKLIST.md +124 -12
- package/CHANGELOG.md +12 -0
- package/README.md +57 -40
- package/TESTING.md +152 -0
- package/dist/blocks/dialog-box/block.json +24 -0
- package/dist/blocks/dialog-box/edit.js +41 -0
- package/dist/blocks/dialog-box/index.js +17 -0
- package/dist/blocks/dialog-box/render.php +24 -0
- package/dist/blocks/dialog-box/save.js +23 -0
- package/dist/blocks/dialog-box/style.css +23 -0
- package/dist/blocks/icon/block.json +24 -0
- package/dist/blocks/icon/edit.js +41 -0
- package/dist/blocks/icon/index.js +17 -0
- package/dist/blocks/icon/render.php +24 -0
- package/dist/blocks/icon/save.js +23 -0
- package/dist/blocks/icon/style.css +23 -0
- package/dist/blocks/input-helper-message/block.json +24 -0
- package/dist/blocks/input-helper-message/edit.js +42 -0
- package/dist/blocks/input-helper-message/index.js +17 -0
- package/dist/blocks/input-helper-message/render.php +24 -0
- package/dist/blocks/input-helper-message/save.js +23 -0
- package/dist/blocks/input-helper-message/style.css +23 -0
- package/dist/blocks/tab/block.json +24 -0
- package/dist/blocks/tab/edit.js +41 -0
- package/dist/blocks/tab/index.js +17 -0
- package/dist/blocks/tab/render.php +24 -0
- package/dist/blocks/tab/save.js +23 -0
- package/dist/blocks/tab/style.css +23 -0
- package/dist/blocks/tab-group/block.json +24 -0
- package/dist/blocks/tab-group/edit.js +41 -0
- package/dist/blocks/tab-group/index.js +17 -0
- package/dist/blocks/tab-group/render.php +24 -0
- package/dist/blocks/tab-group/save.js +23 -0
- package/dist/blocks/tab-group/style.css +23 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.setup.d.ts +1 -0
- package/package.json +21 -5
- package/scripts/create-wordpress-block-tests.cjs +438 -0
- package/scripts/create-wordpress-block.cjs +681 -0
- package/src/blocks/dialog-box/block.json +24 -0
- package/src/blocks/dialog-box/edit.js +41 -0
- package/src/blocks/dialog-box/index.js +17 -0
- package/src/blocks/dialog-box/render.php +24 -0
- package/src/blocks/dialog-box/save.js +23 -0
- package/src/blocks/dialog-box/style.css +23 -0
- package/src/blocks/icon/block.json +24 -0
- package/src/blocks/icon/edit.js +41 -0
- package/src/blocks/icon/index.js +17 -0
- package/src/blocks/icon/render.php +24 -0
- package/src/blocks/icon/save.js +23 -0
- package/src/blocks/icon/style.css +23 -0
- package/src/blocks/input-helper-message/block.json +24 -0
- package/src/blocks/input-helper-message/edit.js +42 -0
- package/src/blocks/input-helper-message/index.js +17 -0
- package/src/blocks/input-helper-message/render.php +24 -0
- package/src/blocks/input-helper-message/save.js +23 -0
- package/src/blocks/input-helper-message/style.css +23 -0
- package/src/blocks/tab/block.json +24 -0
- package/src/blocks/tab/edit.js +41 -0
- package/src/blocks/tab/index.js +17 -0
- package/src/blocks/tab/render.php +24 -0
- package/src/blocks/tab/save.js +23 -0
- package/src/blocks/tab/style.css +23 -0
- package/src/blocks/tab-group/block.json +24 -0
- package/src/blocks/tab-group/edit.js +41 -0
- package/src/blocks/tab-group/index.js +17 -0
- package/src/blocks/tab-group/render.php +24 -0
- package/src/blocks/tab-group/save.js +23 -0
- package/src/blocks/tab-group/style.css +23 -0
- package/vitest.config.js +28 -0
- package/vitest.config.ts +28 -0
- package/vitest.setup.ts +129 -0
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WordPress Block Creation Script
|
|
5
|
+
*
|
|
6
|
+
* Transforms React components to WordPress blocks using the BLOCK_CREATION_CHECKLIST.md
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node scripts/create-wordpress-block.js [component-name]
|
|
10
|
+
* node scripts/create-wordpress-block.js --list
|
|
11
|
+
* node scripts/create-wordpress-block.js --status
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
// Colors for console output
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
bright: '\x1b[1m',
|
|
22
|
+
dim: '\x1b[2m',
|
|
23
|
+
green: '\x1b[32m',
|
|
24
|
+
yellow: '\x1b[33m',
|
|
25
|
+
red: '\x1b[31m',
|
|
26
|
+
cyan: '\x1b[36m',
|
|
27
|
+
blue: '\x1b[34m',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function log(message, color = 'reset') {
|
|
31
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getReactComponents() {
|
|
35
|
+
const srcDir = path.join(__dirname, '../../components-react/src');
|
|
36
|
+
return fs.readdirSync(srcDir)
|
|
37
|
+
.filter(f => {
|
|
38
|
+
const stat = fs.statSync(path.join(srcDir, f));
|
|
39
|
+
return stat.isDirectory() && !f.startsWith('.');
|
|
40
|
+
})
|
|
41
|
+
.filter(f => f !== 'utils' && f !== 'index.js' && f !== 'styles.d.ts')
|
|
42
|
+
.sort();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getWordPressBlocks() {
|
|
46
|
+
const blocksDir = path.join(__dirname, '../src/blocks');
|
|
47
|
+
if (!fs.existsSync(blocksDir)) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return fs.readdirSync(blocksDir)
|
|
51
|
+
.filter(f => {
|
|
52
|
+
const stat = fs.statSync(path.join(blocksDir, f));
|
|
53
|
+
return stat.isDirectory() && !f.startsWith('.');
|
|
54
|
+
})
|
|
55
|
+
.sort();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function componentToBlockName(componentName) {
|
|
59
|
+
// Convert PascalCase to kebab-case
|
|
60
|
+
return componentName
|
|
61
|
+
.replace(/([A-Z])/g, '-$1')
|
|
62
|
+
.toLowerCase()
|
|
63
|
+
.replace(/^-/, '');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getTransformationStatus() {
|
|
67
|
+
const reactComponents = getReactComponents();
|
|
68
|
+
const wordPressBlocks = getWordPressBlocks();
|
|
69
|
+
|
|
70
|
+
const status = {
|
|
71
|
+
transformed: [],
|
|
72
|
+
pending: [],
|
|
73
|
+
blocksWithoutComponent: [],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
reactComponents.forEach(component => {
|
|
77
|
+
const blockName = componentToBlockName(component);
|
|
78
|
+
if (wordPressBlocks.includes(blockName)) {
|
|
79
|
+
status.transformed.push({ component, block: blockName });
|
|
80
|
+
} else {
|
|
81
|
+
status.pending.push({ component, blockName });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
wordPressBlocks.forEach(block => {
|
|
86
|
+
const componentExists = reactComponents.some(c => componentToBlockName(c) === block);
|
|
87
|
+
if (!componentExists) {
|
|
88
|
+
status.blocksWithoutComponent.push(block);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return status;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function showStatus() {
|
|
96
|
+
const status = getTransformationStatus();
|
|
97
|
+
|
|
98
|
+
log('\nš Block Transformation Status', 'cyan');
|
|
99
|
+
log('ā'.repeat(60), 'cyan');
|
|
100
|
+
|
|
101
|
+
log(`\nā
Transformed Components (${status.transformed.length}):`, 'green');
|
|
102
|
+
status.transformed.forEach(({ component, block }) => {
|
|
103
|
+
log(` ${component} ā ${block}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (status.pending.length > 0) {
|
|
107
|
+
log(`\nā³ Pending Transformation (${status.pending.length}):`, 'yellow');
|
|
108
|
+
status.pending.forEach(({ component, blockName }) => {
|
|
109
|
+
log(` ${component} ā ${blockName}`);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (status.blocksWithoutComponent.length > 0) {
|
|
114
|
+
log(`\nā ļø Blocks Without Component (${status.blocksWithoutComponent.length}):`, 'yellow');
|
|
115
|
+
status.blocksWithoutComponent.forEach(block => {
|
|
116
|
+
log(` ${block} (orphaned)`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
log(`\nš Overall: ${status.transformed.length}/${getReactComponents().length} components transformed`, 'bright');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function showAvailable() {
|
|
124
|
+
const status = getTransformationStatus();
|
|
125
|
+
|
|
126
|
+
log('\nš React Components & Transformation Status', 'cyan');
|
|
127
|
+
log('ā'.repeat(60), 'cyan');
|
|
128
|
+
|
|
129
|
+
log('\nā
Already Transformed:', 'green');
|
|
130
|
+
if (status.transformed.length === 0) {
|
|
131
|
+
log(' (none)');
|
|
132
|
+
} else {
|
|
133
|
+
status.transformed.forEach(({ component }) => {
|
|
134
|
+
log(` ā ${component}`);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
log('\nā³ Ready for Transformation:', 'yellow');
|
|
139
|
+
if (status.pending.length === 0) {
|
|
140
|
+
log(' (none - all components have blocks!)');
|
|
141
|
+
} else {
|
|
142
|
+
status.pending.forEach(({ component, blockName }) => {
|
|
143
|
+
log(` ā ${component} ā ${blockName}`);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
log('\nš” To transform a component, run:', 'cyan');
|
|
148
|
+
log(' npm run block:create -- <component-name>', 'dim');
|
|
149
|
+
log('\n Example:', 'dim');
|
|
150
|
+
log(' npm run block:create -- DialogBox', 'dim');
|
|
151
|
+
log('\n Or to see checklists for all pending:', 'dim');
|
|
152
|
+
log(' npm run block:create:all', 'dim');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function showChecklistsAll() {
|
|
156
|
+
const status = getTransformationStatus();
|
|
157
|
+
|
|
158
|
+
if (status.pending.length === 0) {
|
|
159
|
+
log('\nā
All components have blocks! Nothing pending.\n', 'green');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
log(`\nš Checklists for All Pending Components (${status.pending.length})`, 'cyan');
|
|
164
|
+
log('ā'.repeat(60), 'cyan');
|
|
165
|
+
|
|
166
|
+
status.pending.forEach(({ component, blockName }, index) => {
|
|
167
|
+
log(`\n[${index + 1}/${status.pending.length}] ${component} ā ${blockName}`, 'bright');
|
|
168
|
+
log('ā'.repeat(60), 'dim');
|
|
169
|
+
|
|
170
|
+
const checklistPath = path.join(__dirname, '../BLOCK_CREATION_CHECKLIST.md');
|
|
171
|
+
log(`\nš Quick Start:`);
|
|
172
|
+
log(`\n1. Analyze the React component:`);
|
|
173
|
+
log(` - Location: packages/components-react/src/${component}/`);
|
|
174
|
+
log(` - Review props and variants`);
|
|
175
|
+
|
|
176
|
+
log(`\n2. Create block files:`);
|
|
177
|
+
log(` mkdir -p src/blocks/${blockName}`);
|
|
178
|
+
log(` # Create: block.json, index.js, edit.js, save.js, render.php, style.css`);
|
|
179
|
+
|
|
180
|
+
log(`\n3. Follow the checklist`);
|
|
181
|
+
log(`4. Test the block: npm run build`);
|
|
182
|
+
log(`5. Add tests: __tests__/blocks/${blockName}.test.js`);
|
|
183
|
+
|
|
184
|
+
log(`\nš Full Checklist: ${checklistPath}\n`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
log('ā'.repeat(60), 'cyan');
|
|
188
|
+
log(`\nš Summary: ${status.pending.length} components ready for transformation`, 'yellow');
|
|
189
|
+
log(`To execute transformations, run:`, 'dim');
|
|
190
|
+
log(`npm run block:create:all`, 'dim');
|
|
191
|
+
log(`\nOr for a specific component:`, 'dim');
|
|
192
|
+
log(`npm run block:create -- ComponentName\n`, 'dim');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function showChecklistForOne(componentName) {
|
|
196
|
+
const blockName = componentToBlockName(componentName);
|
|
197
|
+
const checklistPath = path.join(__dirname, '../BLOCK_CREATION_CHECKLIST.md');
|
|
198
|
+
|
|
199
|
+
if (!fs.existsSync(checklistPath)) {
|
|
200
|
+
log(`ā Checklist not found: ${checklistPath}`, 'red');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
log(`\nš Block Creation Checklist for ${componentName}`, 'cyan');
|
|
205
|
+
log('ā'.repeat(60), 'cyan');
|
|
206
|
+
log(`\nComponent: ${componentName}`);
|
|
207
|
+
log(`Block Name: tds/${blockName}`);
|
|
208
|
+
log(`Directory: src/blocks/${blockName}/`);
|
|
209
|
+
log(`\nš Reference: ${checklistPath}`, 'dim');
|
|
210
|
+
|
|
211
|
+
log(`\nš Quick Start:`, 'bright');
|
|
212
|
+
log(`\n1. Analyze the React component:`, 'green');
|
|
213
|
+
log(` - Location: packages/components-react/src/${componentName}/`);
|
|
214
|
+
log(` - Review props and variants`);
|
|
215
|
+
|
|
216
|
+
log(`\n2. Create block files:`, 'green');
|
|
217
|
+
log(` mkdir -p src/blocks/${blockName}`);
|
|
218
|
+
log(` # Create: block.json, index.js, edit.js, save.js, render.php, style.css`);
|
|
219
|
+
|
|
220
|
+
log(`\n3. Follow the checklist:`, 'green');
|
|
221
|
+
log(` - Copy React component structure to WordPress block`);
|
|
222
|
+
log(` - Define block.json attributes`);
|
|
223
|
+
log(` - Implement edit.js (editor UI)`);
|
|
224
|
+
log(` - Implement save.js (static output)`);
|
|
225
|
+
log(` - Implement render.php (server-side rendering)`);
|
|
226
|
+
log(` - Copy and adapt styles from React component`);
|
|
227
|
+
log(` - Map CSS custom properties to resolved values`);
|
|
228
|
+
|
|
229
|
+
log(`\n4. Test the block:`, 'green');
|
|
230
|
+
log(` npm run build`);
|
|
231
|
+
log(` # Test in WordPress Gutenberg editor`);
|
|
232
|
+
|
|
233
|
+
log(`\n5. Add tests:`, 'green');
|
|
234
|
+
log(` - Create __tests__/blocks/${blockName}.test.js`);
|
|
235
|
+
log(` - Test all variants and states`);
|
|
236
|
+
|
|
237
|
+
log(`\nš Full Checklist: ${checklistPath}`, 'yellow');
|
|
238
|
+
log('\nOpen the checklist file for detailed requirements.\n');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function executeTransformation(componentName) {
|
|
242
|
+
const blockName = componentToBlockName(componentName);
|
|
243
|
+
const blocksDir = path.join(__dirname, '../src/blocks');
|
|
244
|
+
const blockDir = path.join(blocksDir, blockName);
|
|
245
|
+
const reactCompDir = path.join(__dirname, '../../components-react/src', componentName);
|
|
246
|
+
|
|
247
|
+
// Check if component exists
|
|
248
|
+
if (!fs.existsSync(reactCompDir)) {
|
|
249
|
+
log(`\nā React component not found: ${componentName}`, 'red');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check if block already exists
|
|
254
|
+
if (fs.existsSync(blockDir)) {
|
|
255
|
+
log(`\nā ļø Block already exists: ${blockName}`, 'yellow');
|
|
256
|
+
log(` Location: ${blockDir}`, 'dim');
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Create block directory structure
|
|
261
|
+
fs.mkdirSync(blockDir, { recursive: true });
|
|
262
|
+
|
|
263
|
+
// Generate block.json
|
|
264
|
+
const blockJson = {
|
|
265
|
+
$schema: 'https://schemas.wp.org/trunk/block.json',
|
|
266
|
+
apiVersion: 3,
|
|
267
|
+
name: `tds/${blockName}`,
|
|
268
|
+
title: `Times Design System - ${componentName}`,
|
|
269
|
+
category: 'common',
|
|
270
|
+
description: `WordPress block for Times Design System ${componentName} component`,
|
|
271
|
+
icon: 'block-default',
|
|
272
|
+
supports: {
|
|
273
|
+
html: false,
|
|
274
|
+
multiple: true,
|
|
275
|
+
reusable: true,
|
|
276
|
+
customClassName: true,
|
|
277
|
+
},
|
|
278
|
+
textdomain: 'times-blocks',
|
|
279
|
+
attributes: {
|
|
280
|
+
content: {
|
|
281
|
+
type: 'string',
|
|
282
|
+
default: '',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
render: `file:./render.php`,
|
|
286
|
+
editorScript: `file:./index.js`,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
fs.writeFileSync(
|
|
290
|
+
path.join(blockDir, 'block.json'),
|
|
291
|
+
JSON.stringify(blockJson, null, 2)
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Generate index.js (loader + register)
|
|
295
|
+
const indexJs = `/**
|
|
296
|
+
* WordPress Block Loader: ${componentName}
|
|
297
|
+
*
|
|
298
|
+
* Reference: packages/components-react/src/${componentName}/
|
|
299
|
+
* TODO: Update attributes in block.json based on component props
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
import { registerBlockType } from '@wordpress/blocks';
|
|
303
|
+
import { edit as Edit } from './edit';
|
|
304
|
+
import { save as Save } from './save';
|
|
305
|
+
import blockConfig from './block.json';
|
|
306
|
+
|
|
307
|
+
registerBlockType(blockConfig.name, {
|
|
308
|
+
...blockConfig,
|
|
309
|
+
edit: Edit,
|
|
310
|
+
save: Save,
|
|
311
|
+
});
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
fs.writeFileSync(path.join(blockDir, 'index.js'), indexJs);
|
|
315
|
+
|
|
316
|
+
// Generate edit.js
|
|
317
|
+
const editJs = `/**
|
|
318
|
+
* Block Editor Component: ${componentName}
|
|
319
|
+
*
|
|
320
|
+
* Renders the editor UI for the ${componentName} block in Gutenberg.
|
|
321
|
+
* Reference: packages/components-react/src/${componentName}/
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
|
|
325
|
+
import { PanelBody, TextControl } from '@wordpress/components';
|
|
326
|
+
|
|
327
|
+
export function edit(props) {
|
|
328
|
+
const { attributes, setAttributes } = props;
|
|
329
|
+
const blockProps = useBlockProps();
|
|
330
|
+
|
|
331
|
+
// TODO: Implement editor UI based on component props
|
|
332
|
+
// - Add inspector controls for attributes
|
|
333
|
+
// - Render preview of component in editor
|
|
334
|
+
// - Handle attribute changes via setAttributes()
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<>
|
|
338
|
+
<InspectorControls>
|
|
339
|
+
<PanelBody title="${componentName} Settings" initialOpen={true}>
|
|
340
|
+
<TextControl
|
|
341
|
+
label="Content"
|
|
342
|
+
value={attributes.content || ''}
|
|
343
|
+
onChange={(content) => setAttributes({ content })}
|
|
344
|
+
placeholder="Enter content..."
|
|
345
|
+
/>
|
|
346
|
+
{/* TODO: Add more controls based on component variants */}
|
|
347
|
+
</PanelBody>
|
|
348
|
+
</InspectorControls>
|
|
349
|
+
<div {...blockProps}>
|
|
350
|
+
<p>š ${componentName} block editor</p>
|
|
351
|
+
<p style={{ fontSize: '12px', color: '#666' }}>
|
|
352
|
+
TODO: Render the ${componentName} component here with current attributes
|
|
353
|
+
</p>
|
|
354
|
+
</div>
|
|
355
|
+
</>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
`;
|
|
359
|
+
|
|
360
|
+
fs.writeFileSync(path.join(blockDir, 'edit.js'), editJs);
|
|
361
|
+
|
|
362
|
+
// Generate save.js
|
|
363
|
+
const saveJs = `/**
|
|
364
|
+
* Block Save Component: ${componentName}
|
|
365
|
+
*
|
|
366
|
+
* Renders the static output saved to the database.
|
|
367
|
+
* Reference: packages/components-react/src/${componentName}/
|
|
368
|
+
*/
|
|
369
|
+
|
|
370
|
+
import { useBlockProps } from '@wordpress/block-editor';
|
|
371
|
+
|
|
372
|
+
export function save(props) {
|
|
373
|
+
const { attributes } = props;
|
|
374
|
+
const blockProps = useBlockProps.save();
|
|
375
|
+
|
|
376
|
+
// TODO: Render the actual ${componentName} component with attributes
|
|
377
|
+
// This output is saved to the database and displayed on the frontend
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
<div {...blockProps}>
|
|
381
|
+
{/* TODO: Render ${componentName} component */}
|
|
382
|
+
{attributes.content && <p>{attributes.content}</p>}
|
|
383
|
+
</div>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
`;
|
|
387
|
+
|
|
388
|
+
fs.writeFileSync(path.join(blockDir, 'save.js'), saveJs);
|
|
389
|
+
|
|
390
|
+
// Generate render.php
|
|
391
|
+
const renderPhp = `<?php
|
|
392
|
+
/**
|
|
393
|
+
* Block Rendering: ${componentName}
|
|
394
|
+
*
|
|
395
|
+
* Server-side rendering for the ${componentName} block.
|
|
396
|
+
* Reference: packages/components-react/src/${componentName}/
|
|
397
|
+
*
|
|
398
|
+
* @param array $attributes Block attributes
|
|
399
|
+
* @param string $content Saved block content
|
|
400
|
+
* @param WP_Block $block The block instance
|
|
401
|
+
* @return string Rendered HTML
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
\$wrapper_attributes = get_block_wrapper_attributes();
|
|
405
|
+
|
|
406
|
+
// TODO: Render the ${componentName} component
|
|
407
|
+
// Extract attributes and render appropriate HTML with BEM classes
|
|
408
|
+
|
|
409
|
+
?>
|
|
410
|
+
<div <?php echo wp_kses_post( $wrapper_attributes ); ?>>
|
|
411
|
+
<!-- TODO: Render ${componentName} component here -->
|
|
412
|
+
<!-- Use CSS custom properties from packages/tokens/data/resolved-hexes.json -->
|
|
413
|
+
<!-- Apply BEM naming: tds-${blockName}--{modifier} -->
|
|
414
|
+
</div>
|
|
415
|
+
`;
|
|
416
|
+
|
|
417
|
+
fs.writeFileSync(path.join(blockDir, 'render.php'), renderPhp);
|
|
418
|
+
|
|
419
|
+
// Generate style.css
|
|
420
|
+
const styleCss = `/**
|
|
421
|
+
* Styles for ${componentName} Block
|
|
422
|
+
*
|
|
423
|
+
* Reference: packages/components-react/src/${componentName}/
|
|
424
|
+
* Token Reference: packages/tokens/data/resolved-hexes.json
|
|
425
|
+
*
|
|
426
|
+
* TODO: Adapt styles from React component
|
|
427
|
+
* - Copy relevant CSS from React component
|
|
428
|
+
* - Map CSS custom properties to resolved hex values
|
|
429
|
+
* - Use BEM naming: .tds-${blockName}--{modifier}
|
|
430
|
+
*/
|
|
431
|
+
|
|
432
|
+
.tds-${blockName} {
|
|
433
|
+
/* TODO: Add component styles here */
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.tds-${blockName}--focus {
|
|
437
|
+
/* TODO: Add focus state styles */
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.tds-${blockName}--disabled {
|
|
441
|
+
/* TODO: Add disabled state styles */
|
|
442
|
+
}
|
|
443
|
+
`;
|
|
444
|
+
|
|
445
|
+
fs.writeFileSync(path.join(blockDir, 'style.css'), styleCss);
|
|
446
|
+
|
|
447
|
+
// Create test file
|
|
448
|
+
let testCreated = false;
|
|
449
|
+
try {
|
|
450
|
+
const { createBlockTest } = require('./create-wordpress-block-tests.cjs');
|
|
451
|
+
if (createBlockTest(componentName)) {
|
|
452
|
+
testCreated = true;
|
|
453
|
+
}
|
|
454
|
+
} catch (error) {
|
|
455
|
+
log(` ā ļø Could not auto-generate test: ${error.message}`, 'dim');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Success output
|
|
459
|
+
log(`\nā
Block Created Successfully!`, 'green');
|
|
460
|
+
log('ā'.repeat(60), 'green');
|
|
461
|
+
|
|
462
|
+
log(`\nš¦ Block Details:`, 'bright');
|
|
463
|
+
log(` Name: ${componentName}`);
|
|
464
|
+
log(` Block: tds/${blockName}`);
|
|
465
|
+
log(` Location: ${blockDir}`);
|
|
466
|
+
|
|
467
|
+
log(`\nš Generated Files:`, 'bright');
|
|
468
|
+
log(` ā block.json - Block configuration`);
|
|
469
|
+
log(` ā index.js - Block loader`);
|
|
470
|
+
log(` ā edit.js - Editor component`);
|
|
471
|
+
log(` ā save.js - Save component`);
|
|
472
|
+
log(` ā render.php - Server rendering`);
|
|
473
|
+
log(` ā style.css - Block styles`);
|
|
474
|
+
if (testCreated) {
|
|
475
|
+
log(` ā [test] ${blockName}.test.js - Test file`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
log(`\nš Next Steps:`, 'cyan');
|
|
479
|
+
log(`\n 1. Review the React component:`, 'dim');
|
|
480
|
+
log(` packages/components-react/src/${componentName}/`, 'dim');
|
|
481
|
+
|
|
482
|
+
log(`\n 2. Update block.json attributes:`, 'dim');
|
|
483
|
+
log(` ${blockDir}/block.json`, 'dim');
|
|
484
|
+
|
|
485
|
+
log(`\n 3. Implement editor UI (edit.js):`, 'dim');
|
|
486
|
+
log(` ${blockDir}/edit.js`, 'dim');
|
|
487
|
+
|
|
488
|
+
log(`\n 4. Implement save component (save.js):`, 'dim');
|
|
489
|
+
log(` ${blockDir}/save.js`, 'dim');
|
|
490
|
+
|
|
491
|
+
log(`\n 5. Update server rendering (render.php):`, 'dim');
|
|
492
|
+
log(` ${blockDir}/render.php`, 'dim');
|
|
493
|
+
|
|
494
|
+
log(`\n 6. Add component styles (style.css):`, 'dim');
|
|
495
|
+
log(` ${blockDir}/style.css`, 'dim');
|
|
496
|
+
|
|
497
|
+
log(`\n 7. Create and run tests:`, 'dim');
|
|
498
|
+
log(` __tests__/blocks/${blockName}.test.js`, 'dim');
|
|
499
|
+
|
|
500
|
+
log(`\n 8. Build and test in WordPress:`, 'dim');
|
|
501
|
+
log(` npm run build`, 'dim');
|
|
502
|
+
log(` # Test in WordPress Gutenberg editor`, 'dim');
|
|
503
|
+
|
|
504
|
+
log(`\nš Reference Documentation:`, 'dim');
|
|
505
|
+
log(` - BLOCK_CREATION_CHECKLIST.md`, 'dim');
|
|
506
|
+
log(` - TRANSFORMATION_GUIDE.md`, 'dim');
|
|
507
|
+
log(` - SCSS_VARIABLES_REFERENCE.md`, 'dim');
|
|
508
|
+
log('\n');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function executeAllTransformations() {
|
|
512
|
+
const status = getTransformationStatus();
|
|
513
|
+
|
|
514
|
+
if (status.pending.length === 0) {
|
|
515
|
+
log('\nā
All components already have blocks!', 'green');
|
|
516
|
+
log(' Nothing to transform.\n', 'dim');
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
log(`\nš Executing Block Transformations (${status.pending.length} pending)`, 'cyan');
|
|
521
|
+
log('ā'.repeat(60), 'cyan');
|
|
522
|
+
|
|
523
|
+
const results = {
|
|
524
|
+
created: [],
|
|
525
|
+
skipped: [],
|
|
526
|
+
errors: [],
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
status.pending.forEach(({ component, blockName }, index) => {
|
|
530
|
+
log(`\n[${index + 1}/${status.pending.length}] Transforming ${component}...`, 'bright');
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
executeTransformation(component);
|
|
534
|
+
results.created.push(component);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
log(` ā Error: ${error.message}`, 'red');
|
|
537
|
+
results.errors.push({ component, error: error.message });
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// Summary
|
|
542
|
+
log('\n' + 'ā'.repeat(60), 'cyan');
|
|
543
|
+
log('\nš Transformation Summary:', 'bright');
|
|
544
|
+
log(` ā
Created: ${results.created.length}`, 'green');
|
|
545
|
+
log(` ā ļø Errors: ${results.errors.length}`, results.errors.length > 0 ? 'red' : 'dim');
|
|
546
|
+
|
|
547
|
+
if (results.created.length > 0) {
|
|
548
|
+
log(`\nā Successfully created blocks for:`, 'green');
|
|
549
|
+
results.created.forEach(c => log(` - ${c}`, 'dim'));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (results.errors.length > 0) {
|
|
553
|
+
log(`\nā Errors:`, 'red');
|
|
554
|
+
results.errors.forEach(({ component, error }) => {
|
|
555
|
+
log(` - ${component}: ${error}`, 'dim');
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
log(`\nš” Next: Review generated files and implement component logic`, 'cyan');
|
|
560
|
+
log(` npm run build`, 'dim');
|
|
561
|
+
log('\n');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function showHelp() {
|
|
565
|
+
log('\nš WordPress Block Transformation Tool', 'bright');
|
|
566
|
+
log('ā'.repeat(60));
|
|
567
|
+
|
|
568
|
+
log('\nUsage:', 'cyan');
|
|
569
|
+
log(' npm run block:create [command|component]', 'dim');
|
|
570
|
+
|
|
571
|
+
log('\nCommands:', 'cyan');
|
|
572
|
+
log(' npm run block:status Show transformation status', 'dim');
|
|
573
|
+
log(' npm run block:list List components pending transformation', 'dim');
|
|
574
|
+
log(' npm run block:create -- NAME Execute transformation for specific component', 'dim');
|
|
575
|
+
log(' npm run block:create:all Execute transformations for all pending', 'dim');
|
|
576
|
+
log(' npm run block:checklist -- NAME Show detailed checklist (no transformation)', 'dim');
|
|
577
|
+
log(' npm run block:checklist:all Show all checklists (no transformation)', 'dim');
|
|
578
|
+
log(' npm run block:create -- --help Show this help message', 'dim');
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
log('\nExamples:', 'cyan');
|
|
582
|
+
log('\n š Check status:', 'dim');
|
|
583
|
+
log(' npm run block:status', 'dim');
|
|
584
|
+
|
|
585
|
+
log('\n š List pending components:', 'dim');
|
|
586
|
+
log(' npm run block:list', 'dim');
|
|
587
|
+
|
|
588
|
+
log('\n ā¶ļø Execute transformation for one component:', 'dim');
|
|
589
|
+
log(' npm run block:create -- DialogBox', 'dim');
|
|
590
|
+
|
|
591
|
+
log('\n ā¶ļø Execute transformations for all pending:', 'dim');
|
|
592
|
+
log(' npm run block:create:all', 'dim');
|
|
593
|
+
|
|
594
|
+
log('\n š Show checklist for one component (no transformation):', 'dim');
|
|
595
|
+
log(' npm run block:checklist -- DialogBox', 'dim');
|
|
596
|
+
|
|
597
|
+
log('\n š Show checklists for all pending (no transformation):', 'dim');
|
|
598
|
+
log(' npm run block:checklist:all', 'dim');
|
|
599
|
+
|
|
600
|
+
log('\nReference Files:', 'cyan');
|
|
601
|
+
log(' - BLOCK_CREATION_CHECKLIST.md Step-by-step transformation guide', 'dim');
|
|
602
|
+
log(' - TRANSFORMATION_GUIDE.md Component transformation patterns', 'dim');
|
|
603
|
+
log(' - SCSS_VARIABLES_REFERENCE.md Design token value mappings', 'dim');
|
|
604
|
+
|
|
605
|
+
log('\n');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function main() {
|
|
609
|
+
const args = process.argv.slice(2);
|
|
610
|
+
|
|
611
|
+
if (args.length === 0) {
|
|
612
|
+
showHelp();
|
|
613
|
+
process.exit(0);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const command = args[0].toLowerCase();
|
|
617
|
+
const reactComponents = getReactComponents();
|
|
618
|
+
|
|
619
|
+
switch (command) {
|
|
620
|
+
case '--help':
|
|
621
|
+
case '-h':
|
|
622
|
+
showHelp();
|
|
623
|
+
break;
|
|
624
|
+
|
|
625
|
+
case '--status':
|
|
626
|
+
case '-s':
|
|
627
|
+
showStatus();
|
|
628
|
+
break;
|
|
629
|
+
|
|
630
|
+
case '--list':
|
|
631
|
+
case '-l':
|
|
632
|
+
showAvailable();
|
|
633
|
+
break;
|
|
634
|
+
|
|
635
|
+
// Execute transformations
|
|
636
|
+
case '--all':
|
|
637
|
+
case '-a':
|
|
638
|
+
executeAllTransformations();
|
|
639
|
+
break;
|
|
640
|
+
|
|
641
|
+
// Show checklists (no transformation)
|
|
642
|
+
case '--checklist:all':
|
|
643
|
+
case '-ca':
|
|
644
|
+
showChecklistsAll();
|
|
645
|
+
break;
|
|
646
|
+
|
|
647
|
+
case '--checklist':
|
|
648
|
+
case '-c':
|
|
649
|
+
if (args.length < 2) {
|
|
650
|
+
log(`\nā Component name required with --checklist`, 'red');
|
|
651
|
+
log(`\nUsage: npm run block:checklist -- ComponentName`, 'dim');
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
const checklistComponent = args[1];
|
|
655
|
+
if (reactComponents.includes(checklistComponent)) {
|
|
656
|
+
showChecklistForOne(checklistComponent);
|
|
657
|
+
} else {
|
|
658
|
+
log(`\nā Component not found: ${checklistComponent}`, 'red');
|
|
659
|
+
log(`\nAvailable React components:`, 'yellow');
|
|
660
|
+
reactComponents.forEach(c => log(` - ${c}`));
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
break;
|
|
664
|
+
|
|
665
|
+
default:
|
|
666
|
+
// Default: assume it's a component name and execute transformation
|
|
667
|
+
const componentName = args[0];
|
|
668
|
+
|
|
669
|
+
if (reactComponents.includes(componentName)) {
|
|
670
|
+
executeTransformation(componentName);
|
|
671
|
+
} else {
|
|
672
|
+
log(`\nā Component not found: ${componentName}`, 'red');
|
|
673
|
+
log(`\nAvailable React components:`, 'yellow');
|
|
674
|
+
reactComponents.forEach(c => log(` - ${c}`));
|
|
675
|
+
log(`\nRun with --help for more information\n`);
|
|
676
|
+
process.exit(1);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
main();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schemas.wp.org/trunk/block.json",
|
|
3
|
+
"apiVersion": 3,
|
|
4
|
+
"name": "tds/dialog-box",
|
|
5
|
+
"title": "Times Design System - DialogBox",
|
|
6
|
+
"category": "common",
|
|
7
|
+
"description": "WordPress block for Times Design System DialogBox component",
|
|
8
|
+
"icon": "block-default",
|
|
9
|
+
"supports": {
|
|
10
|
+
"html": false,
|
|
11
|
+
"multiple": true,
|
|
12
|
+
"reusable": true,
|
|
13
|
+
"customClassName": true
|
|
14
|
+
},
|
|
15
|
+
"textdomain": "times-blocks",
|
|
16
|
+
"attributes": {
|
|
17
|
+
"content": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"default": ""
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"render": "file:./render.php",
|
|
23
|
+
"editorScript": "file:./index.js"
|
|
24
|
+
}
|