@andersundsehr/storybook-typo3 0.1.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/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@andersundsehr/storybook-typo3",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./src/index.ts",
8
+ "types": "./src/index.ts"
9
+ },
10
+ "./preset": {
11
+ "require": "./src/preset.ts",
12
+ "types": "./src/preset.ts"
13
+ }
14
+ },
15
+ "dependencies": {
16
+ "@storybook/builder-vite": "^9.0.0",
17
+ "@storybook/server": "^9.0.0",
18
+ "@storybook/addon-docs": "^9.0.0",
19
+ "@storybook/addon-a11y": "^9.0.0",
20
+ "storybook": "^9.0.0"
21
+ },
22
+ "scripts": {
23
+ "test": "node --test --experimental-strip-types",
24
+ "test:watch": "node --test --experimental-strip-types --watch"
25
+ }
26
+ }
@@ -0,0 +1,31 @@
1
+ import { SNIPPET_RENDERED, SourceType } from 'storybook/internal/docs-tools';
2
+ import { type FluidRenderer } from '@andersundsehr/storybook-typo3';
3
+ import { DecoratorFunction } from 'storybook/internal/types';
4
+ import { addons, useEffect } from 'storybook/preview-api';
5
+ import { convertComponentToSource } from './functions/convertComponentToSource';
6
+
7
+ const sourceDecorator: DecoratorFunction<FluidRenderer> = (storyFn, storyContext) => {
8
+ useEffect(() => {
9
+ const { id, args, component } = storyContext;
10
+ const source = convertComponentToSource(component, args);
11
+ addons.getChannel().emit(SNIPPET_RENDERED, { id, args, source });
12
+ });
13
+
14
+ return storyFn();
15
+ };
16
+
17
+ export const tags = ['autodocs'];
18
+
19
+ export const decorators: DecoratorFunction<FluidRenderer>[] = [sourceDecorator];
20
+ export const parameters = {
21
+ docs: {
22
+ story: {
23
+ inline: true,
24
+ },
25
+ codePanel: true,
26
+ source: {
27
+ type: SourceType.DYNAMIC,
28
+ },
29
+ toc: true,
30
+ },
31
+ };
@@ -0,0 +1,21 @@
1
+ import { fetchPreviewConfig, fetchRenderAction, type StoryContext } from '@andersundsehr/storybook-typo3';
2
+ import type { ArgTypesEnhancer, GlobalTypes } from 'storybook/internal/types';
3
+ import { initGlobalsHandling } from './functions/fetchPreviewConfig.ts';
4
+
5
+ const previewConfig = await fetchPreviewConfig();
6
+
7
+ initGlobalsHandling(previewConfig.globalTypes);
8
+
9
+ export const globalTypes: GlobalTypes = previewConfig.globalTypes;
10
+
11
+ export const initialGlobals: Record<string, string> = previewConfig.initialGlobals;
12
+
13
+ export const parameters = {
14
+ server: {
15
+ // url: url + '/_storybook/',
16
+ fetchStoryHtml: fetchRenderAction,
17
+ },
18
+ };
19
+
20
+ const enhanceArgTypes: ArgTypesEnhancer = (storyContext: StoryContext) => storyContext.component?.argTypes || {};
21
+ export const argTypesEnhancers: ArgTypesEnhancer[] = [enhanceArgTypes];
@@ -0,0 +1,126 @@
1
+ import test from 'node:test';
2
+ import { convertComponentToSource } from './convertComponentToSource.ts';
3
+ import type { FluidComponent } from '../types/types.ts';
4
+ import type { StrictArgs } from 'storybook/internal/csf';
5
+
6
+ interface TestCase { component: FluidComponent; args: StrictArgs; expected: string }
7
+
8
+ const component = { name: 'test', fullName: 'c:test', argTypes: {}, collection: 'Class', namespace: 'c' };
9
+ const testCases: Record<string, TestCase> = {
10
+ ///////////////////////////////////////////
11
+ withoutAnyProperties: {
12
+ component,
13
+ args: {},
14
+ expected: `<html
15
+ xmlns:c="http://typo3.org/ns/Class"
16
+ data-namespace-typo3-fluid="true"
17
+ >
18
+
19
+ <c:test/>
20
+
21
+ <!-- or -->
22
+
23
+ {namespace c=Class}
24
+
25
+ {c:test()}`,
26
+ },
27
+ ///////////////////////////////////////////
28
+ withSimpleArguments: {
29
+ component,
30
+ args: { arg1: 'value1', arg2: 'value2' },
31
+ expected: `<html
32
+ xmlns:c="http://typo3.org/ns/Class"
33
+ data-namespace-typo3-fluid="true"
34
+ >
35
+
36
+ <c:test arg1="value1" arg2="value2"/>
37
+
38
+ <!-- or -->
39
+
40
+ {namespace c=Class}
41
+
42
+ {c:test(arg1: 'value1', arg2: 'value2')}`,
43
+ },
44
+ ///////////////////////////////////////////
45
+ withSlot: {
46
+ component,
47
+ args: { arg1: 'value\'"1', arg2: 'value2', slot____default: 'default \'"slot' },
48
+ expected: `<html
49
+ xmlns:c="http://typo3.org/ns/Class"
50
+ data-namespace-typo3-fluid="true"
51
+ >
52
+
53
+ <c:test arg1="value'\\"1" arg2="value2">
54
+ default '"slot
55
+ </c:test>
56
+
57
+ <!-- or -->
58
+
59
+ {namespace c=Class}
60
+
61
+ {'default \\'"slot' -> c:test(arg1: 'value\\'"1', arg2: 'value2')}`,
62
+ },
63
+ ///////////////////////////////////////////
64
+ withMultipleSlots: {
65
+ component,
66
+ args: { slot____a: 'slotA', slot____b: 'slotB', slot____default: 'slotDefault' },
67
+ expected: `<html
68
+ xmlns:c="http://typo3.org/ns/Class"
69
+ data-namespace-typo3-fluid="true"
70
+ >
71
+
72
+ <c:test>
73
+ <f:fragment name="a">slotA</f:fragment>
74
+ <f:fragment name="b">slotB</f:fragment>
75
+ <f:fragment name="default">slotDefault</f:fragment>
76
+ </c:test>`,
77
+ },
78
+ ///////////////////////////////////////////
79
+ withTransformedArgument: {
80
+ component,
81
+ args: { arg1__argA: 'value1', arg1__argB: 'value2' },
82
+ expected: `<html
83
+ xmlns:c="http://typo3.org/ns/Class"
84
+ data-namespace-typo3-fluid="true"
85
+ >
86
+
87
+ <c:test arg1="{arg1}"/>
88
+
89
+ <!-- or -->
90
+
91
+ {namespace c=Class}
92
+
93
+ {c:test(arg1: arg1)}`,
94
+ },
95
+ ///////////////////////////////////////////
96
+ longLines: {
97
+ component,
98
+ args: { arg1: 'veryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValue', arg2: 'veryLongValueveryLongValueveryLongValueveryLongValueveryLongValue' },
99
+ expected: `<html
100
+ xmlns:c="http://typo3.org/ns/Class"
101
+ data-namespace-typo3-fluid="true"
102
+ >
103
+
104
+ <c:test
105
+ arg1="veryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValue"
106
+ arg2="veryLongValueveryLongValueveryLongValueveryLongValueveryLongValue"
107
+ />
108
+
109
+ <!-- or -->
110
+
111
+ {namespace c=Class}
112
+
113
+ {c:test(
114
+ arg1: 'veryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValueveryLongValue',
115
+ arg2: 'veryLongValueveryLongValueveryLongValueveryLongValueveryLongValue'
116
+ )}`,
117
+ },
118
+ ///////////////////////////////////////////
119
+ };
120
+
121
+ for (const [name, { component, args, expected }] of Object.entries(testCases)) {
122
+ test(`convertComponentToSource - ${name}`, (t) => {
123
+ const result = convertComponentToSource(component, args);
124
+ t.assert.strictEqual(result, expected);
125
+ });
126
+ }
@@ -0,0 +1,151 @@
1
+ import type { FluidComponent } from '../types/types';
2
+ import type { StrictArgs } from 'storybook/internal/csf';
3
+
4
+ function convertValueToString(key: string, argType: { type?: { name?: 'int' } }, value: ArgumentValue, inline: boolean): string {
5
+ const createValue = () => {
6
+ if ('variableName' in value) {
7
+ if (inline) {
8
+ return value.variableName; // For inline code, just return the variable name
9
+ }
10
+ return '{' + value.variableName + '}'; // For attributes, return the variable name wrapped in curly braces
11
+ }
12
+ const valueAsString = String(value.value);
13
+ if (typeof value.value === 'boolean') {
14
+ if (inline) {
15
+ return valueAsString;
16
+ }
17
+ return '{' + valueAsString + '}';
18
+ }
19
+ if (typeof value.value === 'number') {
20
+ if (argType?.type?.name === 'int') {
21
+ return String(Number.parseInt(valueAsString, 10)); // Convert to integer
22
+ }
23
+ return String(Number.parseFloat(valueAsString)); // Convert to float
24
+ }
25
+ if (inline) {
26
+ return '\'' + valueAsString.replace(/'/g, '\\\'') + '\''; // Escape single quotes for inline code
27
+ }
28
+ return valueAsString.replace(/"/g, '\\"'); // Escape double quotes for attributes
29
+ };
30
+
31
+ if (inline) {
32
+ return key + ': ' + createValue();
33
+ }
34
+ return key + '="' + createValue() + '"';
35
+ }
36
+
37
+ function inlineCode(viewHelperArguments: Arguments, component: FluidComponent, defaultSlotContent?: string) {
38
+ const argsStrings: string[] = Object.entries(viewHelperArguments)
39
+ .map(([argumentName, argumentValue]) => convertValueToString(argumentName, component.argTypes[argumentName], argumentValue, true));
40
+ const argsString = argsStrings.join(', ');
41
+
42
+ let source = '{';
43
+
44
+ if (defaultSlotContent) {
45
+ defaultSlotContent = defaultSlotContent.replace(/'/g, '\\\''); // Escape single quotes for inline code
46
+ source += `'${defaultSlotContent}' -> `;
47
+ }
48
+ source += `${component.fullName}(`;
49
+ if ((source.length + argsString.length) > 80) {
50
+ source += `\n ${argsStrings.join(',\n ')}\n`;
51
+ } else {
52
+ source += argsString;
53
+ }
54
+
55
+ source += ')}';
56
+ return source;
57
+ }
58
+
59
+ function generateOpenTag(viewHelperArguments: Arguments, component: FluidComponent) {
60
+ const argStrings: string[] = Object.entries(viewHelperArguments)
61
+ .map(([argumentName, argumentValue]) => convertValueToString(argumentName, component.argTypes[argumentName], argumentValue, false));
62
+ let argsString = argStrings.join(' ');
63
+ let openTag = `<${component.fullName}`;
64
+ if (argsString.length > 0) {
65
+ openTag += ' ' + argsString;
66
+ }
67
+ if (openTag.length > 80) {
68
+ argsString = argStrings.join('\n ');
69
+ openTag = `<${component.fullName} \n ${argsString}\n`;
70
+ }
71
+ return openTag;
72
+ }
73
+
74
+ const SLOT_PREFIX = 'slot____';
75
+
76
+ type ArgumentValue = {
77
+ value: unknown;
78
+ } | {
79
+ variableName: string;
80
+ };
81
+
82
+ type Arguments = Record<string, ArgumentValue>;
83
+ type Slots = Record<string, string>;
84
+ interface ComponentData { viewHelperArguments: Arguments; slots: Slots }
85
+
86
+ function createComponentData(component: FluidComponent, args: StrictArgs): ComponentData {
87
+ const viewHelperArguments: Arguments = {};
88
+ const slots: Slots = {};
89
+
90
+ for (const [key, value] of Object.entries(args)) {
91
+ if (key.startsWith(SLOT_PREFIX)) {
92
+ // This is a slot
93
+ const slotName = key.replace(SLOT_PREFIX, '');
94
+ if (value === null || value === undefined || value === '') {
95
+ continue;
96
+ }
97
+
98
+ slots[slotName] = value as string;
99
+ continue;
100
+ }
101
+
102
+ if (!key.includes('__')) {
103
+ // This is a normal argument without virtual subkey
104
+ viewHelperArguments[key] = { value };
105
+ continue;
106
+ }
107
+
108
+ // This is a virtual argument
109
+ const [variableName] = key.split('__');
110
+ viewHelperArguments[variableName] = { variableName };
111
+ }
112
+
113
+ return { viewHelperArguments, slots };
114
+ }
115
+
116
+ export function convertComponentToSource(component: FluidComponent, args: StrictArgs): string {
117
+ const { viewHelperArguments, slots } = createComponentData(component, args);
118
+
119
+ let source = '';
120
+ source += `<html\n xmlns:${component.namespace}="http://typo3.org/ns/${component.collection.replace(/\\/g, '/')}"\n data-namespace-typo3-fluid="true"\n>\n\n`;
121
+
122
+ const usedArgs: string[] = [...new Set(Object.keys(args).filter(key => !key.startsWith(SLOT_PREFIX)).map(key => key.split('__')[0]))];
123
+
124
+ source += generateOpenTag(viewHelperArguments, component);
125
+
126
+ if (Object.keys(slots).length <= 0) {
127
+ source += `/>\n`;
128
+ source += `\n<!-- or -->\n\n`;
129
+ source += `{namespace ${component.namespace}=${component.collection}}\n\n`;
130
+ source += inlineCode(viewHelperArguments, component);
131
+ return source;
132
+ }
133
+ source += '>\n';
134
+
135
+ if (Object.keys(slots).length === 1 && slots.default) {
136
+ // If there is only a default slot, we can use the inline code
137
+ source += ` ${slots.default}\n`;
138
+ source += '</' + component.fullName + '>\n';
139
+ source += `\n<!-- or -->\n\n`;
140
+ source += `{namespace ${component.namespace}=${component.collection}}\n\n`;
141
+ source += inlineCode(viewHelperArguments, component, slots.default);
142
+ return source;
143
+ }
144
+
145
+ for (const [slotName, slotValue] of Object.entries(slots)) {
146
+ source += ` <f:fragment name="${slotName}">${slotValue}</f:fragment>\n`;
147
+ }
148
+ source += '</' + component.fullName + '>';
149
+
150
+ return source;
151
+ }
@@ -0,0 +1,30 @@
1
+ import { type FluidComponent, url } from '@andersundsehr/storybook-typo3';
2
+
3
+ import { fetchWithUserRetry } from './fetchWithUserRetry.ts';
4
+
5
+ interface ComponentMetaData {
6
+ collectionClassName: string;
7
+ argTypes: Record<string, any>;
8
+ }
9
+
10
+ export async function fetchComponent(component: string): Promise<FluidComponent> {
11
+ if (!component.includes(':')) {
12
+ const message = 'Component name must be in the format "namespace:name"';
13
+ alert(message);
14
+ throw new Error(message);
15
+ }
16
+
17
+ const data = await fetchWithUserRetry<ComponentMetaData>(
18
+ url + '/_storybook/componentMeta?viewHelper=' + component,
19
+ {},
20
+ 'metadata for component `' + component + '` from TYPO3',
21
+ );
22
+
23
+ return {
24
+ fullName: component,
25
+ name: component.split(':')[1],
26
+ namespace: component.split(':')[0],
27
+ collection: data.collectionClassName,
28
+ argTypes: data.argTypes,
29
+ };
30
+ }
@@ -0,0 +1,86 @@
1
+ import { url } from '@andersundsehr/storybook-typo3';
2
+ import { fetchWithUserRetry } from './fetchWithUserRetry.ts';
3
+ import type { GlobalTypes, InputType } from 'storybook/internal/types';
4
+ import { GLOBALS_UPDATED, SET_GLOBALS } from 'storybook/internal/core-events';
5
+ import { addons } from 'storybook/preview-api';
6
+
7
+ function getLocation(): Location {
8
+ let location = window.location;
9
+ if (window.parent?.location && window.parent !== window) {
10
+ location = window.parent.location;
11
+ }
12
+ return location;
13
+ }
14
+
15
+ function getGlobalsFromUrl(): Record<string, string> {
16
+ const url = new URL(getLocation().href);
17
+ const globalsString = url.searchParams.get('globals') || '';
18
+ const globalsArray = globalsString.split(';').filter(Boolean);
19
+ const entries = globalsArray.map(global => global.split(':'));
20
+ return Object.fromEntries(entries);
21
+ }
22
+
23
+ function setGlobalsToUrl(globals: Record<string, string>) {
24
+ const location = getLocation();
25
+ const url = new URL(location.href);
26
+ const globalString = Object.entries(globals).map(([key, value]) => `${key}:${value}`).join(';');
27
+ url.searchParams.set('globals', globalString);
28
+ location.href = url.toString();
29
+ }
30
+
31
+ interface PreviewConfig {
32
+ globalTypes: GlobalTypes;
33
+ initialGlobals: Record<string, string>;
34
+ }
35
+
36
+ export async function fetchPreviewConfig(currentGlobals?: Record<string, string>): Promise<PreviewConfig> {
37
+ const apiEndpoint = url + '/_storybook/preview';
38
+
39
+ const globals = currentGlobals || getGlobalsFromUrl();
40
+
41
+ return await fetchWithUserRetry<PreviewConfig>(
42
+ apiEndpoint,
43
+ {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ },
48
+ body: JSON.stringify({ globals }),
49
+ },
50
+ 'preview config from TYPO3',
51
+ );
52
+ }
53
+
54
+ export function initGlobalsHandling(initalGlobalTypes: GlobalTypes) {
55
+ const channel = addons.getChannel();
56
+ let oldGlobalTypes = initalGlobalTypes;
57
+ channel.on(GLOBALS_UPDATED, async ({ globals }: { globals: Record<string, string> }) => {
58
+ const previewConfig = await fetchPreviewConfig(globals);
59
+
60
+ oldGlobalTypes = previewConfig.globalTypes;
61
+
62
+ const newGlobals: Record<string, string> = getGlobalsFromUrl();
63
+
64
+ let changed = false;
65
+ for (const key in previewConfig.globalTypes) {
66
+ const inputType: InputType = previewConfig.globalTypes[key];
67
+ if (!inputType.toolbar?.items?.some(item => item.value === globals[key])) {
68
+ newGlobals[key] = inputType.toolbar?.items?.[0]?.value || '';
69
+ changed = true;
70
+ }
71
+ }
72
+
73
+ if (changed) {
74
+ // change URL search params and reload page (this is the only way to set the globals, that worked)!!
75
+ setGlobalsToUrl(newGlobals);
76
+ return;
77
+ }
78
+
79
+ if (JSON.stringify(oldGlobalTypes) !== JSON.stringify(previewConfig.globalTypes)) {
80
+ // this only sets the globalTypes and not the globals :(
81
+ // see https://github.com/storybookjs/storybook/blob/3ac2fece4c41955e7349f6f825b7d21dd18ff9a9/code/core/src/manager-api/modules/globals.ts#L141-L149
82
+ channel.emit(SET_GLOBALS, { globals, globalTypes: previewConfig.globalTypes });
83
+ return;
84
+ }
85
+ });
86
+ }
@@ -0,0 +1,19 @@
1
+ import { url, type StoryContext } from '@andersundsehr/storybook-typo3';
2
+
3
+ import { fetchWithUserRetry } from './fetchWithUserRetry.ts';
4
+
5
+ export async function fetchRenderAction(urlA: string, id: string, params: any, storyContext: StoryContext): Promise<string> {
6
+ const viewHelper = (typeof storyContext.component === 'string') ? storyContext.component : storyContext.component.fullName;
7
+
8
+ const body = {
9
+ viewHelper: viewHelper,
10
+ arguments: params,
11
+ site: storyContext.globals?.site || 'default',
12
+ siteLanguage: storyContext.globals?.language || 'default',
13
+ };
14
+
15
+ return await fetchWithUserRetry<string>(url + '/_storybook/render', {
16
+ method: 'POST',
17
+ body: JSON.stringify(body),
18
+ }, 'rendering component in TYPO3', 'text');
19
+ };
@@ -0,0 +1,24 @@
1
+ export async function fetchWithUserRetry<T>(url: string, options: RequestInit, message: string, result: 'json' | 'text' = 'json'): Promise<T> {
2
+ options = { ...options }; // Clone options to avoid mutating the original
3
+ options.signal = options.signal || AbortSignal.timeout(5000);
4
+ try {
5
+ const response = await fetch(url, options);
6
+ if (result === 'text') {
7
+ return await response.text() as T;
8
+ }
9
+ return await response.json() as T;
10
+ } catch (error) {
11
+ console.error('Fetch failed:', { error });
12
+ const retry = confirm(
13
+ 'Error while fetching ' + message + '. \n\n'
14
+ + 'fetch: ' + url + '\n'
15
+ + 'ERROR: ' + String(error) + '\n\n'
16
+ + 'look at the console for more details.\n\n'
17
+ + 'Do you want to retry?',
18
+ );
19
+ if (retry) {
20
+ return fetchWithUserRetry(url, options, message);
21
+ }
22
+ throw error; // Re-throw the error if the user does not want to retry
23
+ }
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export * from './functions/fetchRenderAction.ts';
2
+ export * from './functions/fetchPreviewConfig.ts';
3
+ export * from './functions/fetchComponent';
4
+ export * from './functions/fetchWithUserRetry.ts';
5
+ export * from './types/types';
6
+
7
+ const url = (() => {
8
+ let url = import.meta.env.STORYBOOK_TYPO3_ENDPOINT;
9
+
10
+ if (typeof url !== 'string' || !url) {
11
+ throw new Error('env STORYBOOK_TYPO3_ENDPOINT is not set or is not a string');
12
+ }
13
+
14
+ url = url.replace(/_storybook\/?$/, '');
15
+ url = url.replace(/\/$/, '');
16
+
17
+ if (typeof url !== 'string' || !url) {
18
+ throw new Error('env STORYBOOK_TYPO3_ENDPOINT is not set or is not a string');
19
+ }
20
+ return url;
21
+ })();
22
+
23
+ export { url };
package/src/preset.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { dirname, join } from 'node:path';
2
+ import type { Entry, PresetProperty } from 'storybook/internal/types';
3
+
4
+ function getAbsolutePath<I extends string>(value: I): I {
5
+ return dirname(require.resolve(join(value, 'package.json'))) as any;
6
+ }
7
+
8
+ export const addons = [
9
+ '@storybook/addon-docs',
10
+ '@storybook/addon-a11y',
11
+ ];
12
+
13
+ export const core: PresetProperty<'core'> = {
14
+ builder: getAbsolutePath('@storybook/builder-vite'),
15
+ renderer: getAbsolutePath('@storybook/server'),
16
+ disableTelemetry: true,
17
+ };
18
+
19
+ export const previewAnnotations: PresetProperty<'previewAnnotations'> = async (entry: Entry[] = [], options) => {
20
+ const docsEnabled = Object.keys(await options.presets.apply('docs', {}, options)).length > 0;
21
+
22
+ return entry
23
+ .concat(require.resolve('./entry-preview'))
24
+ .concat(docsEnabled ? [require.resolve('./entry-preview-docs')] : []);
25
+ };
26
+
27
+ export const tags = ['autodocs'];
28
+
29
+ /**
30
+ * BUGFIX for chromium based browsers and windows
31
+ * @see https://github.com/talkjs/country-flag-emoji-polyfill?tab=readme-ov-file
32
+ */
33
+ export const managerHead = `<style>
34
+ body {
35
+ font-family: "Twemoji Country Flags", "Nunito Sans", -apple-system, ".SFNSText-Regular", "San Francisco", BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif !important;
36
+ }
37
+ @font-face {
38
+ font-family: "Twemoji Country Flags";
39
+ unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
40
+ src: url('https://cdn.jsdelivr.net/npm/country-flag-emoji-polyfill@0.1/dist/TwemojiCountryFlags.woff2') format('woff2');
41
+ font-display: swap;
42
+ }
43
+ </style>`;
@@ -0,0 +1,89 @@
1
+ // export type { StoryObj } from '@storybook/server';
2
+ import type { Args, StrictArgs } from '@storybook/server';
3
+ import {
4
+ AnnotatedStoryFn,
5
+ CompatibleString,
6
+ ComponentAnnotations,
7
+ DecoratorFunction,
8
+ LoaderFunction,
9
+ ProjectAnnotations,
10
+ StoryAnnotations,
11
+ StorybookConfig as StorybookConfigBase,
12
+ StoryContext as StoryContext$1,
13
+ WebRenderer,
14
+ } from 'storybook/internal/types';
15
+
16
+ import type { BuilderOptions, StorybookConfigVite } from '@storybook/builder-vite';
17
+
18
+ type FrameworkName = CompatibleString<'@andersundsehr/storybook-typo3'>;
19
+ type BuilderName = CompatibleString<'@storybook/builder-vite'>;
20
+
21
+ export interface FrameworkOptions {
22
+ builder?: BuilderOptions;
23
+ }
24
+
25
+ interface StorybookConfigFramework {
26
+ framework:
27
+ | FrameworkName
28
+ | {
29
+ name: FrameworkName;
30
+ options: FrameworkOptions;
31
+ };
32
+ core?: StorybookConfigBase['core'] & {
33
+ builder?:
34
+ | BuilderName
35
+ | {
36
+ name: BuilderName;
37
+ options: BuilderOptions;
38
+ };
39
+ };
40
+ }
41
+
42
+ /** The interface for Storybook configuration in `main.ts` files. */
43
+ export type StorybookConfig = Omit<
44
+ StorybookConfigBase,
45
+ keyof StorybookConfigVite | keyof StorybookConfigFramework
46
+ > &
47
+ StorybookConfigVite &
48
+ StorybookConfigFramework;
49
+
50
+ type StoryFnServerReturnType = any;
51
+
52
+ export interface FluidComponent {
53
+ fullName: string;
54
+ name: string;
55
+ namespace: string;
56
+ collection: string;
57
+ argTypes: Record<string, any>;
58
+ }
59
+
60
+ interface FluidRenderer extends WebRenderer {
61
+ component: FluidComponent;
62
+ storyResult: StoryFnServerReturnType;
63
+ }
64
+
65
+ /**
66
+ * Metadata to configure the stories for a component.
67
+ *
68
+ * @see [Default export](https://storybook.js.org/docs/api/csf#default-export)
69
+ */
70
+ type Meta<TArgs = Args> = ComponentAnnotations<FluidRenderer, TArgs>;
71
+ /**
72
+ * Story function that represents a CSFv2 component example.
73
+ *
74
+ * @see [Named Story exports](https://storybook.js.org/docs/api/csf#named-story-exports)
75
+ */
76
+ type StoryFn<TArgs = Args> = AnnotatedStoryFn<FluidRenderer, TArgs>;
77
+ /**
78
+ * Story object that represents a CSFv3 component example.
79
+ *
80
+ * @see [Named Story exports](https://storybook.js.org/docs/api/csf#named-story-exports)
81
+ */
82
+ type StoryObj<TArgs = Args> = StoryAnnotations<FluidRenderer, TArgs>;
83
+
84
+ type Decorator<TArgs = StrictArgs> = DecoratorFunction<FluidRenderer, TArgs>;
85
+ type Loader<TArgs = StrictArgs> = LoaderFunction<FluidRenderer, TArgs>;
86
+ type StoryContext<TArgs = StrictArgs> = StoryContext$1<FluidRenderer, TArgs>;
87
+ type Preview = ProjectAnnotations<FluidRenderer>;
88
+
89
+ export { Decorator, Loader, Meta, Preview, FluidRenderer, StoryContext, StoryFn, StoryObj };