Skip to content

Commit 8c72e31

Browse files
authored
Merge pull request #18750 from craftcms/feature/plugins-page
[6.x] Plugin settings to inertia
2 parents ab99350 + d9cf65e commit 8c72e31

36 files changed

Lines changed: 2630 additions & 174 deletions

.storybook/inertia-mock.ts

Lines changed: 437 additions & 0 deletions
Large diffs are not rendered by default.

.storybook/main.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type {StorybookConfig} from '@storybook/vue3-vite';
2+
import {dirname, join} from 'path';
3+
import vue from '@vitejs/plugin-vue';
4+
import tailwindcss from '@tailwindcss/vite';
5+
6+
/**
7+
* This function is used to resolve the absolute path of a package.
8+
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
9+
*/
10+
function getAbsolutePath(value: string): string {
11+
return dirname(require.resolve(join(value, 'package.json')));
12+
}
13+
14+
const config: StorybookConfig = {
15+
stories: ['../resources/js/**/*.mdx', '../resources/js/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
16+
addons: [
17+
getAbsolutePath('@storybook/addon-themes'),
18+
getAbsolutePath('@storybook/addon-docs'),
19+
getAbsolutePath('@storybook/addon-a11y'),
20+
],
21+
framework: {
22+
name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite',
23+
options: {
24+
docgen: 'vue-component-meta',
25+
},
26+
},
27+
viteFinal(config) {
28+
// Storybook's vue3-vite framework adds its own Vue plugin with default options.
29+
// We need to configure `isCustomElement` so Vue treats `craft-*` tags as web
30+
// components (from @craftcms/cp) rather than trying to resolve them as Vue
31+
// components. Since Vite's mergeConfig doesn't deep-merge plugin options,
32+
// we remove Storybook's Vue plugin and add our own with the correct config.
33+
const filteredPlugins = (config.plugins || []).flat().filter((plugin) => {
34+
if (plugin && typeof plugin === 'object' && 'name' in plugin) {
35+
return plugin.name !== 'vite:vue';
36+
}
37+
return true;
38+
});
39+
40+
return {
41+
...config,
42+
plugins: [
43+
...filteredPlugins,
44+
tailwindcss(),
45+
vue({
46+
template: {
47+
compilerOptions: {
48+
isCustomElement: (tag) => tag.startsWith('craft-'),
49+
},
50+
},
51+
}),
52+
],
53+
resolve: {
54+
...config.resolve,
55+
alias: {
56+
...(config.resolve?.alias || {}),
57+
'@': join(__dirname, '../resources/js'),
58+
vue: 'vue/dist/vue.esm-bundler.js',
59+
// Mock Inertia for Storybook
60+
'@inertiajs/vue3': join(__dirname, 'inertia-mock.ts'),
61+
},
62+
},
63+
};
64+
},
65+
};
66+
67+
export default config;

.storybook/preview.css

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* Storybook preview utilities */
2+
3+
/* Layout helpers for stories */
4+
.sb-flex {
5+
display: flex;
6+
}
7+
8+
.sb-flex-col {
9+
flex-direction: column;
10+
}
11+
12+
.sb-items-center {
13+
align-items: center;
14+
}
15+
16+
.sb-justify-center {
17+
justify-content: center;
18+
}
19+
20+
.sb-gap-2 {
21+
gap: 0.5rem;
22+
}
23+
24+
.sb-gap-4 {
25+
gap: 1rem;
26+
}
27+
28+
.sb-p-4 {
29+
padding: 1rem;
30+
}
31+
32+
/* Grid utilities */
33+
.sb-grid {
34+
display: grid;
35+
}
36+
37+
.sb-grid-cols-2 {
38+
grid-template-columns: repeat(2, 1fr);
39+
}
40+
41+
.sb-grid-cols-3 {
42+
grid-template-columns: repeat(3, 1fr);
43+
}
44+
45+
/* Stack layout */
46+
.sb-stack {
47+
display: flex;
48+
flex-direction: column;
49+
gap: 1rem;
50+
}
51+
52+
.sb-inline {
53+
display: flex;
54+
flex-wrap: wrap;
55+
gap: 0.5rem;
56+
align-items: center;
57+
}

.storybook/preview.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type {Preview} from '@storybook/vue3';
2+
import {setup} from '@storybook/vue3';
3+
import {withThemeByDataAttribute} from '@storybook/addon-themes';
4+
import '@craftcms/cp';
5+
import '../resources/css/cp.css';
6+
import './preview.css';
7+
import {installInertiaMock, type PageProps, setPageProps} from './inertia-mock';
8+
9+
// Install the Inertia mock globally
10+
setup((app) => {
11+
installInertiaMock(app);
12+
});
13+
14+
// Declare module augmentation for Storybook parameters
15+
declare module '@storybook/vue3' {
16+
interface Parameters {
17+
inertia?: Partial<PageProps>;
18+
}
19+
}
20+
21+
const preview: Preview = {
22+
parameters: {
23+
controls: {
24+
expanded: true,
25+
matchers: {
26+
color: /(background|color)$/i,
27+
date: /Date$/i,
28+
},
29+
},
30+
options: {
31+
storySort: {
32+
method: 'alphabetical',
33+
},
34+
},
35+
a11y: {
36+
// 'todo' - show a11y violations in the test UI only
37+
// 'error' - fail CI on a11y violations
38+
// 'off' - skip a11y checks entirely
39+
test: 'todo',
40+
},
41+
},
42+
decorators: [
43+
// Inertia page props decorator - must come before theme decorator
44+
(story, context) => {
45+
// Reset and apply any story-specific page props
46+
const inertiaProps = context.parameters.inertia || {};
47+
setPageProps(inertiaProps);
48+
return story();
49+
},
50+
withThemeByDataAttribute({
51+
themes: {
52+
light: 'light',
53+
dark: 'dark',
54+
},
55+
defaultTheme: 'light',
56+
attributeName: 'data-theme',
57+
}),
58+
],
59+
tags: ['autodocs'],
60+
};
61+
62+
export default preview;

0 commit comments

Comments
 (0)