generator-bitloops 0.3.27 → 0.3.29
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/package.json +1 -1
- package/setup/index.js +282 -37
- package/setup/templates/.storybook/main.ts +32 -0
- package/setup/templates/.storybook/preview.tsx +58 -0
- package/setup/templates/.storybook/vitest.setup.ts +7 -0
- package/setup/templates/platform-next/src/Img.tsx +6 -5
- package/setup/templates/platform-next/src/Link.tsx +6 -0
- package/setup/templates/platform-next/src/index.ts +3 -2
- package/setup/templates/platform-next/src/router/index.ts +1 -0
- package/setup/templates/platform-next/src/router/useNextRouter.ts +14 -0
- package/setup/templates/platform-next/src/setup.ts +7 -0
- package/setup/templates/platform-vite/src/Img.tsx +18 -0
- package/setup/templates/platform-vite/src/Link.tsx +19 -0
- package/setup/templates/platform-vite/src/index.ts +3 -0
- package/setup/templates/platform-vite/src/router/index.ts +1 -0
- package/setup/templates/platform-vite/src/router/useViteRouter.ts +17 -0
- package/setup/templates/platform-vite/src/setup.ts +5 -0
- package/setup/templates/platform-next/src/types.ts +0 -28
- package/setup/templates/storybook.preview.ts +0 -15
package/package.json
CHANGED
package/setup/index.js
CHANGED
|
@@ -10,8 +10,11 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
10
10
|
const __dirname = path.dirname(__filename);
|
|
11
11
|
|
|
12
12
|
const DOT = '.';
|
|
13
|
+
const STORYBOOK_FOLDER = '.storybook';
|
|
13
14
|
const PLATFORM_NEXT_FOLDER = 'platform-next';
|
|
14
15
|
const PLATFORM_NEXT_SRC_FOLDER = `${PLATFORM_NEXT_FOLDER}/src`;
|
|
16
|
+
const PLATFORM_VITE_FOLDER = 'platform-vite';
|
|
17
|
+
const PLATFORM_VITE_SRC_FOLDER = `${PLATFORM_VITE_FOLDER}/src`;
|
|
15
18
|
|
|
16
19
|
function isKebabCase(str) {
|
|
17
20
|
// Check if the string is empty
|
|
@@ -124,6 +127,54 @@ export default class extends Generator {
|
|
|
124
127
|
default: false,
|
|
125
128
|
});
|
|
126
129
|
|
|
130
|
+
this.option('redux', {
|
|
131
|
+
type: Boolean,
|
|
132
|
+
description: 'Add Redux Toolkit and React Redux for state management',
|
|
133
|
+
default: false,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
this.option('vitest', {
|
|
137
|
+
type: Boolean,
|
|
138
|
+
description: 'Add Vitest testing framework with coverage and UI',
|
|
139
|
+
default: false,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
this.option('webVitals', {
|
|
143
|
+
type: Boolean,
|
|
144
|
+
description: 'Add web-vitals for performance monitoring',
|
|
145
|
+
default: false,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
this.option('zod', {
|
|
149
|
+
type: Boolean,
|
|
150
|
+
description: 'Add Zod for schema validation',
|
|
151
|
+
default: false,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
this.option('bundleAnalyzer', {
|
|
155
|
+
type: Boolean,
|
|
156
|
+
description: 'Add @next/bundle-analyzer for bundle analysis',
|
|
157
|
+
default: false,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
this.option('reactIcons', {
|
|
161
|
+
type: Boolean,
|
|
162
|
+
description: 'Add react-icons library',
|
|
163
|
+
default: false,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.option('msw', {
|
|
167
|
+
type: Boolean,
|
|
168
|
+
description: 'Add Mock Service Worker (MSW) for API mocking',
|
|
169
|
+
default: false,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
this.option('reactCompiler', {
|
|
173
|
+
type: Boolean,
|
|
174
|
+
description: 'Add babel-plugin-react-compiler',
|
|
175
|
+
default: false,
|
|
176
|
+
});
|
|
177
|
+
|
|
127
178
|
this.installNextJS = async function () {
|
|
128
179
|
// Clone Next.js template with Tailwind if specified, using the project name
|
|
129
180
|
const createNextAppCommand = ['-y', 'create-next-app@latest'];
|
|
@@ -212,11 +263,10 @@ export default class extends Generator {
|
|
|
212
263
|
);
|
|
213
264
|
this.log('Storybook installed!');
|
|
214
265
|
// Verifies the correct nextjs-vite framework is used
|
|
215
|
-
spawnSync(
|
|
216
|
-
'
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
266
|
+
spawnSync('pnpm', ['add', '-D', '@storybook/nextjs-vite@^10'], {
|
|
267
|
+
stdio: 'inherit',
|
|
268
|
+
cwd: this.destinationRoot(),
|
|
269
|
+
});
|
|
220
270
|
this.log('@storybook/nextjs-vite installed!');
|
|
221
271
|
}
|
|
222
272
|
};
|
|
@@ -250,11 +300,10 @@ export default class extends Generator {
|
|
|
250
300
|
// Conditionally add i18n packages
|
|
251
301
|
if (this.options.i18n) {
|
|
252
302
|
this.log('Installing i18n packages...');
|
|
253
|
-
spawnSync(
|
|
254
|
-
'
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
);
|
|
303
|
+
spawnSync('pnpm', ['add', 'i18next', 'i18next-icu', 'react-i18next'], {
|
|
304
|
+
stdio: 'inherit',
|
|
305
|
+
cwd: this.destinationRoot(),
|
|
306
|
+
});
|
|
258
307
|
this.log('i18n packages installed!');
|
|
259
308
|
}
|
|
260
309
|
};
|
|
@@ -263,12 +312,139 @@ export default class extends Generator {
|
|
|
263
312
|
// Conditionally add Base UI
|
|
264
313
|
if (this.options.baseUi) {
|
|
265
314
|
this.log('Installing Base UI...');
|
|
315
|
+
spawnSync('pnpm', ['add', '@base-ui/react@^1.1.0'], {
|
|
316
|
+
stdio: 'inherit',
|
|
317
|
+
cwd: this.destinationRoot(),
|
|
318
|
+
});
|
|
319
|
+
this.log('Base UI installed!');
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
this.installRedux = function () {
|
|
324
|
+
// Conditionally add Redux Toolkit and React Redux
|
|
325
|
+
if (this.options.redux) {
|
|
326
|
+
this.log('Installing Redux Toolkit and React Redux...');
|
|
327
|
+
spawnSync('pnpm', ['add', '@reduxjs/toolkit', 'react-redux'], {
|
|
328
|
+
stdio: 'inherit',
|
|
329
|
+
cwd: this.destinationRoot(),
|
|
330
|
+
});
|
|
331
|
+
this.log('Redux Toolkit and React Redux installed!');
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
this.installVitest = function () {
|
|
336
|
+
// Conditionally add Vitest and related testing packages
|
|
337
|
+
if (this.options.vitest) {
|
|
338
|
+
this.log('Installing Vitest and testing packages...');
|
|
266
339
|
spawnSync(
|
|
267
340
|
'pnpm',
|
|
268
|
-
[
|
|
341
|
+
[
|
|
342
|
+
'add',
|
|
343
|
+
'-D',
|
|
344
|
+
'vitest',
|
|
345
|
+
'@vitest/ui',
|
|
346
|
+
'@vitest/coverage-v8',
|
|
347
|
+
'@vitest/browser-playwright',
|
|
348
|
+
'@testing-library/react',
|
|
349
|
+
'@testing-library/jest-dom',
|
|
350
|
+
'@testing-library/user-event',
|
|
351
|
+
'@vitejs/plugin-react',
|
|
352
|
+
'vite',
|
|
353
|
+
'jsdom',
|
|
354
|
+
'playwright',
|
|
355
|
+
],
|
|
269
356
|
{ stdio: 'inherit', cwd: this.destinationRoot() },
|
|
270
357
|
);
|
|
271
|
-
this.log('
|
|
358
|
+
this.log('Vitest and testing packages installed!');
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
this.installWebVitals = function () {
|
|
363
|
+
// Conditionally add web-vitals
|
|
364
|
+
if (this.options.webVitals) {
|
|
365
|
+
this.log('Installing web-vitals...');
|
|
366
|
+
spawnSync('pnpm', ['add', 'web-vitals'], {
|
|
367
|
+
stdio: 'inherit',
|
|
368
|
+
cwd: this.destinationRoot(),
|
|
369
|
+
});
|
|
370
|
+
this.log('web-vitals installed!');
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
this.installZod = function () {
|
|
375
|
+
// Conditionally add Zod
|
|
376
|
+
if (this.options.zod) {
|
|
377
|
+
this.log('Installing Zod...');
|
|
378
|
+
spawnSync('pnpm', ['add', 'zod'], {
|
|
379
|
+
stdio: 'inherit',
|
|
380
|
+
cwd: this.destinationRoot(),
|
|
381
|
+
});
|
|
382
|
+
this.log('Zod installed!');
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
this.installBundleAnalyzer = function () {
|
|
387
|
+
// Conditionally add @next/bundle-analyzer
|
|
388
|
+
if (this.options.bundleAnalyzer) {
|
|
389
|
+
this.log('Installing @next/bundle-analyzer...');
|
|
390
|
+
spawnSync('pnpm', ['add', '-D', '@next/bundle-analyzer'], {
|
|
391
|
+
stdio: 'inherit',
|
|
392
|
+
cwd: this.destinationRoot(),
|
|
393
|
+
});
|
|
394
|
+
this.log('@next/bundle-analyzer installed!');
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
this.installReactIcons = function () {
|
|
399
|
+
// Conditionally add react-icons
|
|
400
|
+
if (this.options.reactIcons) {
|
|
401
|
+
this.log('Installing react-icons...');
|
|
402
|
+
spawnSync('pnpm', ['add', 'react-icons'], {
|
|
403
|
+
stdio: 'inherit',
|
|
404
|
+
cwd: this.destinationRoot(),
|
|
405
|
+
});
|
|
406
|
+
this.log('react-icons installed!');
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
this.installMsw = function () {
|
|
411
|
+
// Conditionally add Mock Service Worker
|
|
412
|
+
if (this.options.msw) {
|
|
413
|
+
this.log('Installing Mock Service Worker (MSW)...');
|
|
414
|
+
spawnSync('pnpm', ['add', '-D', 'msw'], {
|
|
415
|
+
stdio: 'inherit',
|
|
416
|
+
cwd: this.destinationRoot(),
|
|
417
|
+
});
|
|
418
|
+
// Initialize MSW
|
|
419
|
+
spawnSync('npx', ['msw', 'init', 'public/', '--save'], {
|
|
420
|
+
stdio: 'inherit',
|
|
421
|
+
cwd: this.destinationRoot(),
|
|
422
|
+
});
|
|
423
|
+
this.log('MSW installed!');
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
this.installReactCompiler = function () {
|
|
428
|
+
// Conditionally add babel-plugin-react-compiler
|
|
429
|
+
if (this.options.reactCompiler) {
|
|
430
|
+
this.log('Installing babel-plugin-react-compiler...');
|
|
431
|
+
spawnSync('pnpm', ['add', '-D', 'babel-plugin-react-compiler'], {
|
|
432
|
+
stdio: 'inherit',
|
|
433
|
+
cwd: this.destinationRoot(),
|
|
434
|
+
});
|
|
435
|
+
this.log('babel-plugin-react-compiler installed!');
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
this.installIntlMessageFormat = function () {
|
|
440
|
+
// Add intl-messageformat when i18n is enabled
|
|
441
|
+
if (this.options.i18n) {
|
|
442
|
+
this.log('Installing intl-messageformat...');
|
|
443
|
+
spawnSync('pnpm', ['add', 'intl-messageformat'], {
|
|
444
|
+
stdio: 'inherit',
|
|
445
|
+
cwd: this.destinationRoot(),
|
|
446
|
+
});
|
|
447
|
+
this.log('intl-messageformat installed!');
|
|
272
448
|
}
|
|
273
449
|
};
|
|
274
450
|
|
|
@@ -277,26 +453,41 @@ export default class extends Generator {
|
|
|
277
453
|
if (this.options.primitives) {
|
|
278
454
|
this.log('Installing Primitives...');
|
|
279
455
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.destinationPath(
|
|
292
|
-
|
|
456
|
+
// Platform Next files
|
|
457
|
+
const platformNextFiles = [
|
|
458
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/index.ts`,
|
|
459
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/Img.tsx`,
|
|
460
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/Link.tsx`,
|
|
461
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/setup.ts`,
|
|
462
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/router/index.ts`,
|
|
463
|
+
`${PLATFORM_NEXT_SRC_FOLDER}/router/useNextRouter.ts`,
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
platformNextFiles.forEach((filePath) => {
|
|
467
|
+
deleteFileIfExists(this.destinationPath(filePath));
|
|
468
|
+
this.fs.copyTpl(
|
|
469
|
+
this.templatePath(filePath),
|
|
470
|
+
this.destinationPath(filePath),
|
|
471
|
+
);
|
|
472
|
+
});
|
|
293
473
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
474
|
+
// Platform Vite files
|
|
475
|
+
const platformViteFiles = [
|
|
476
|
+
`${PLATFORM_VITE_SRC_FOLDER}/index.ts`,
|
|
477
|
+
`${PLATFORM_VITE_SRC_FOLDER}/Img.tsx`,
|
|
478
|
+
`${PLATFORM_VITE_SRC_FOLDER}/Link.tsx`,
|
|
479
|
+
`${PLATFORM_VITE_SRC_FOLDER}/setup.ts`,
|
|
480
|
+
`${PLATFORM_VITE_SRC_FOLDER}/router/index.ts`,
|
|
481
|
+
`${PLATFORM_VITE_SRC_FOLDER}/router/useViteRouter.ts`,
|
|
482
|
+
];
|
|
483
|
+
|
|
484
|
+
platformViteFiles.forEach((filePath) => {
|
|
485
|
+
deleteFileIfExists(this.destinationPath(filePath));
|
|
486
|
+
this.fs.copyTpl(
|
|
487
|
+
this.templatePath(filePath),
|
|
488
|
+
this.destinationPath(filePath),
|
|
489
|
+
);
|
|
490
|
+
});
|
|
300
491
|
|
|
301
492
|
this.log('Primitives installed!');
|
|
302
493
|
}
|
|
@@ -306,14 +497,25 @@ export default class extends Generator {
|
|
|
306
497
|
// Conditionally initialize Storybook
|
|
307
498
|
if (this.options.storybook) {
|
|
308
499
|
this.log('Making Storybook changes...');
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
500
|
+
|
|
501
|
+
// Copy .storybook template files
|
|
502
|
+
const storybookFiles = [
|
|
503
|
+
`${STORYBOOK_FOLDER}/main.ts`,
|
|
504
|
+
`${STORYBOOK_FOLDER}/preview.tsx`,
|
|
505
|
+
`${STORYBOOK_FOLDER}/vitest.setup.ts`,
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
// Delete .storybook/preview.ts if it exists (generated by storybook init)
|
|
509
|
+
deleteFileIfExists(this.destinationPath(`${STORYBOOK_FOLDER}/preview.ts`));
|
|
510
|
+
|
|
511
|
+
storybookFiles.forEach((filePath) => {
|
|
512
|
+
deleteFileIfExists(this.destinationPath(filePath));
|
|
312
513
|
this.fs.copyTpl(
|
|
313
|
-
this.templatePath(
|
|
314
|
-
this.destinationPath(
|
|
514
|
+
this.templatePath(filePath),
|
|
515
|
+
this.destinationPath(filePath),
|
|
315
516
|
);
|
|
316
|
-
}
|
|
517
|
+
});
|
|
518
|
+
|
|
317
519
|
this.log('Removing default Storybook stories...');
|
|
318
520
|
try {
|
|
319
521
|
fs.rmSync(this.destinationPath('src/stories'), {
|
|
@@ -402,6 +604,39 @@ export default class extends Generator {
|
|
|
402
604
|
}
|
|
403
605
|
};
|
|
404
606
|
|
|
607
|
+
this.patchPackageJsonScripts = async function () {
|
|
608
|
+
this.log('Patching package.json scripts...');
|
|
609
|
+
const packageJsonPath = this.destinationPath('package.json');
|
|
610
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
611
|
+
|
|
612
|
+
// Add vitest scripts if vitest is enabled
|
|
613
|
+
if (this.options.vitest) {
|
|
614
|
+
packageJson.scripts.test = 'vitest';
|
|
615
|
+
packageJson.scripts['test:ui'] = 'vitest --ui';
|
|
616
|
+
packageJson.scripts['test:coverage'] = 'vitest run --coverage';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Add type-check script if typescript is enabled
|
|
620
|
+
if (this.options.typescript) {
|
|
621
|
+
packageJson.scripts['type-check'] = 'tsc --noEmit';
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Add analyze script if bundleAnalyzer is enabled
|
|
625
|
+
if (this.options.bundleAnalyzer) {
|
|
626
|
+
packageJson.scripts.analyze = 'ANALYZE=true next build';
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Add msw configuration if msw is enabled
|
|
630
|
+
if (this.options.msw) {
|
|
631
|
+
packageJson.msw = {
|
|
632
|
+
workerDirectory: ['public'],
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
637
|
+
this.log('package.json scripts patched!');
|
|
638
|
+
};
|
|
639
|
+
|
|
405
640
|
this.commitChanges = async function () {
|
|
406
641
|
this.log('Committing changes to git...');
|
|
407
642
|
await new Promise((resolve) => {
|
|
@@ -448,10 +683,20 @@ export default class extends Generator {
|
|
|
448
683
|
await this.installNextJS();
|
|
449
684
|
this.installCypress();
|
|
450
685
|
this.installI18n();
|
|
686
|
+
this.installIntlMessageFormat();
|
|
451
687
|
this.installBaseUi();
|
|
688
|
+
this.installRedux();
|
|
689
|
+
this.installVitest();
|
|
690
|
+
this.installWebVitals();
|
|
691
|
+
this.installZod();
|
|
692
|
+
this.installBundleAnalyzer();
|
|
693
|
+
this.installReactIcons();
|
|
694
|
+
this.installMsw();
|
|
695
|
+
this.installReactCompiler();
|
|
452
696
|
this.installPrimitives();
|
|
453
697
|
this.installStorybook();
|
|
454
698
|
await this.patchFiles();
|
|
699
|
+
await this.patchPackageJsonScripts();
|
|
455
700
|
if (this.options.git) {
|
|
456
701
|
await this.commitChanges();
|
|
457
702
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { StorybookConfig } from '@storybook/nextjs-vite';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
const config: StorybookConfig = {
|
|
8
|
+
"stories": [
|
|
9
|
+
"../src/**/*.mdx",
|
|
10
|
+
"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
|
|
11
|
+
],
|
|
12
|
+
"addons": [
|
|
13
|
+
"@chromatic-com/storybook",
|
|
14
|
+
"@storybook/addon-vitest",
|
|
15
|
+
"@storybook/addon-a11y",
|
|
16
|
+
"@storybook/addon-docs"
|
|
17
|
+
],
|
|
18
|
+
"framework": "@storybook/nextjs-vite",
|
|
19
|
+
"staticDirs": [
|
|
20
|
+
"../public"
|
|
21
|
+
],
|
|
22
|
+
async viteFinal(config) {
|
|
23
|
+
if (config.resolve) {
|
|
24
|
+
config.resolve.alias = {
|
|
25
|
+
...config.resolve.alias,
|
|
26
|
+
'@/primitives': path.resolve(__dirname, '../platform-vite/src'),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return config;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export default config;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Preview } from "@storybook/nextjs-vite";
|
|
3
|
+
import {
|
|
4
|
+
fontConfig,
|
|
5
|
+
generateGoogleFontsUrl,
|
|
6
|
+
} from "../src/lib/fonts/fonts.config";
|
|
7
|
+
import "../platform-vite/src/setup";
|
|
8
|
+
import "../src/app/globals.css";
|
|
9
|
+
import "../src/i18n";
|
|
10
|
+
import { worker } from "../src/mocks/browser";
|
|
11
|
+
import { Provider } from "react-redux";
|
|
12
|
+
import { store } from "../src/store";
|
|
13
|
+
|
|
14
|
+
await worker.start({ onUnhandledRequest: "bypass" });
|
|
15
|
+
|
|
16
|
+
// Inject Google Fonts stylesheet and CSS variables
|
|
17
|
+
if (typeof document !== "undefined") {
|
|
18
|
+
const link = document.createElement("link");
|
|
19
|
+
link.rel = "stylesheet";
|
|
20
|
+
link.href = generateGoogleFontsUrl();
|
|
21
|
+
document.head.appendChild(link);
|
|
22
|
+
|
|
23
|
+
// Set CSS variables
|
|
24
|
+
const style = document.createElement("style");
|
|
25
|
+
style.textContent = `
|
|
26
|
+
:root {
|
|
27
|
+
${fontConfig.montserrat.variable}: '${fontConfig.montserrat.family}', system-ui, sans-serif;
|
|
28
|
+
${fontConfig.roboto.variable}: '${fontConfig.roboto.family}', system-ui, sans-serif;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
document.head.appendChild(style);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const preview: Preview = {
|
|
35
|
+
parameters: {
|
|
36
|
+
controls: {
|
|
37
|
+
matchers: {
|
|
38
|
+
color: /(background|color)$/i,
|
|
39
|
+
date: /Date$/i,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
a11y: {
|
|
43
|
+
// 'todo' - show a11y violations in the test UI only
|
|
44
|
+
// 'error' - fail CI on a11y violations
|
|
45
|
+
// 'off' - skip a11y checks entirely
|
|
46
|
+
test: "todo",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
decorators: [
|
|
50
|
+
(Story) => (
|
|
51
|
+
<Provider store={store}>
|
|
52
|
+
<Story />
|
|
53
|
+
</Provider>
|
|
54
|
+
),
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default preview;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
|
2
|
+
import { setProjectAnnotations } from '@storybook/nextjs-vite';
|
|
3
|
+
import * as projectAnnotations from './preview';
|
|
4
|
+
|
|
5
|
+
// This is an important step to apply the right configuration when testing your stories.
|
|
6
|
+
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
|
7
|
+
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import NextImage from
|
|
3
|
-
import type { ImgProps } from
|
|
1
|
+
"use client";
|
|
2
|
+
import NextImage from "next/image";
|
|
3
|
+
import type { ImgProps } from "../../src/lib/types/primitives.types";
|
|
4
|
+
|
|
4
5
|
export function Img(props: ImgProps) {
|
|
5
|
-
const {
|
|
6
|
+
const { ...rest } = props;
|
|
6
7
|
return <NextImage {...rest} />;
|
|
7
|
-
}
|
|
8
|
+
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "../../src/lib/types/primitives.types";
|
|
2
|
+
export * from "./Img";
|
|
3
|
+
export * from "./Link";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useNextRouter } from './useNextRouter';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRouter as useNextNavigationRouter } from 'next/navigation';
|
|
4
|
+
import type { Router } from '@/lib/router/types';
|
|
5
|
+
|
|
6
|
+
export function useNextRouter(): Router {
|
|
7
|
+
const nextRouter = useNextNavigationRouter();
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
push: (url: string) => nextRouter.push(url),
|
|
11
|
+
replace: (url: string) => nextRouter.replace(url),
|
|
12
|
+
back: () => nextRouter.back(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable @next/next/no-img-element */
|
|
2
|
+
import type { ImgProps } from "../../src/lib/types/primitives.types";
|
|
3
|
+
|
|
4
|
+
export function Img(props: ImgProps) {
|
|
5
|
+
const { src, alt, className, style, loading, decoding } =
|
|
6
|
+
props;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<img
|
|
10
|
+
src={src}
|
|
11
|
+
alt={alt}
|
|
12
|
+
className={className}
|
|
13
|
+
style={style}
|
|
14
|
+
loading={loading}
|
|
15
|
+
decoding={decoding}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LinkProps } from "../../src/lib/types/primitives.types";
|
|
2
|
+
|
|
3
|
+
export function Link(props: LinkProps) {
|
|
4
|
+
const { href, children, target, rel, className, style, onClick } = props;
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<a
|
|
8
|
+
href={href}
|
|
9
|
+
target={target}
|
|
10
|
+
rel={rel}
|
|
11
|
+
className={className}
|
|
12
|
+
style={style}
|
|
13
|
+
onClick={onClick}
|
|
14
|
+
aria-label={props["aria-label"]}
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</a>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useViteRouter } from "./useViteRouter";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Router } from "@/lib/router/types";
|
|
2
|
+
|
|
3
|
+
export function useViteRouter(): Router {
|
|
4
|
+
return {
|
|
5
|
+
push: (url: string) => {
|
|
6
|
+
console.log("[Storybook Router] Push to:", url);
|
|
7
|
+
// In Storybook, we don't actually navigate
|
|
8
|
+
// This could be enhanced with Storybook actions addon
|
|
9
|
+
},
|
|
10
|
+
replace: (url: string) => {
|
|
11
|
+
console.log("[Storybook Router] Replace with:", url);
|
|
12
|
+
},
|
|
13
|
+
back: () => {
|
|
14
|
+
console.log("[Storybook Router] Go back");
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export type ImgProps = {
|
|
2
|
-
src: string; alt: string;
|
|
3
|
-
width?: number; height?: number; fill?: boolean;
|
|
4
|
-
sizes?: string; priority?: boolean; quality?: number;
|
|
5
|
-
className?: string; style?: React.CSSProperties;
|
|
6
|
-
loading?: 'eager'|'lazy'; decoding?: 'auto'|'sync'|'async';
|
|
7
|
-
responsive?: { sources: Array<{ media: string; srcSet: string }> }; // optional <picture>
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type LinkProps = {
|
|
11
|
-
href: string; children: React.ReactNode;
|
|
12
|
-
prefetch?: boolean; replace?: boolean; scroll?: boolean;
|
|
13
|
-
target?: React.HTMLAttributeAnchorTarget; rel?: string;
|
|
14
|
-
className?: string; style?: React.CSSProperties; 'aria-label'?: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type MetaProps = {
|
|
18
|
-
title?: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
lang?: string;
|
|
21
|
-
openGraph?: { title?: string; description?: string; image?: string; url?: string; type?: string };
|
|
22
|
-
twitter?: { card?: string; title?: string; description?: string; image?: string };
|
|
23
|
-
icons?: { icon?: string; apple?: string };
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export type FontSpec =
|
|
27
|
-
| { kind: 'next'; fonts: Array<{ variable: string; loader: () => any }> } // Next native loaders
|
|
28
|
-
| { kind: 'css'; hrefs: string[]; className?: string };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Preview } from "@storybook/react";
|
|
2
|
-
import "../src/app/globals.css";
|
|
3
|
-
|
|
4
|
-
const preview: Preview = {
|
|
5
|
-
parameters: {
|
|
6
|
-
controls: {
|
|
7
|
-
matchers: {
|
|
8
|
-
color: /(background|color)$/i,
|
|
9
|
-
date: /Date$/i,
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export default preview;
|