diff --git a/app/react-native/scripts/__snapshots__/loader.test.js.snap b/app/react-native/scripts/__snapshots__/loader.test.js.snap index a17424bcc9..56e69f0ec0 100644 --- a/app/react-native/scripts/__snapshots__/loader.test.js.snap +++ b/app/react-native/scripts/__snapshots__/loader.test.js.snap @@ -1,5 +1,44 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`loader writeRequires when there are different file extensions writes the story imports 1`] = ` +" + /* do not change this file, it is auto generated by storybook. */ + + import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native'; + + import \\"@storybook/addon-ondevice-notes/register\\"; +import \\"@storybook/addon-ondevice-controls/register\\"; +import \\"@storybook/addon-ondevice-backgrounds/register\\"; +import \\"@storybook/addon-ondevice-actions/register\\"; + + import { argsEnhancers } from \\"@storybook/addon-actions/dist/modern/preset/addArgs\\" + + + import { decorators, parameters } from './preview'; + + if (decorators) { + decorators.forEach((decorator) => addDecorator(decorator)); + } + + if (parameters) { + addParameters(parameters); + } + + + // temporary fix for https://github.com/storybookjs/react-native/issues/327 whilst the issue is investigated + try { + argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer)); + } catch{} + + + const getStories=() => { + return [require(\\"./FakeStory.stories.tsx\\")]; + } + + configure(getStories, module, false) + " +`; + exports[`loader writeRequires when there is a story glob and exclude paths globs writes the story imports 1`] = ` " /* do not change this file, it is auto generated by storybook. */ diff --git a/app/react-native/scripts/loader.js b/app/react-native/scripts/loader.js index ca87ec7d20..c06abc24a3 100644 --- a/app/react-native/scripts/loader.js +++ b/app/react-native/scripts/loader.js @@ -4,6 +4,7 @@ const glob = require('glob'); const prettier = require('prettier'); const cwd = process.cwd(); +const supportedExtensions = ['js', 'jsx', 'ts', 'tsx', 'cjs', 'mjs']; const previewImports = ` import { decorators, parameters } from './preview'; @@ -38,28 +39,34 @@ function requireUncached(module) { } function getMain({ configPath }) { - const mainPath = path.resolve(cwd, configPath, 'main.js'); + const fileExtension = getFilePathExtension({ configPath }, 'main'); + if (fileExtension === null) { + throw new Error('main config file not found'); + } + const mainPath = path.resolve(cwd, configPath, `main.${fileExtension}`); + return requireUncached(mainPath); } -function getPreviewExists({ configPath }) { - const supportedExtensions = ['js', 'jsx', 'ts', 'tsx']; - +function getFilePathExtension({ configPath }, fileName) { for (const ext of supportedExtensions) { - const previewPath = path.resolve(cwd, configPath, `preview.${ext}`); - - if (fs.existsSync(previewPath)) { - return true; + const filePath = path.resolve(cwd, configPath, `${fileName}.${ext}`); + if (fs.existsSync(filePath)) { + return ext; } } + return null; +} - return false; +function getPreviewExists({ configPath }) { + return !!getFilePathExtension({ configPath }, 'preview'); } function writeRequires({ configPath, absolute = false }) { const storybookRequiresLocation = path.resolve(cwd, configPath, 'storybook.requires.js'); - const main = getMain({ configPath }); + const mainImport = getMain({ configPath }); + const main = mainImport.default ?? mainImport; const reactNativeOptions = main.reactNativeOptions; const excludePaths = reactNativeOptions && reactNativeOptions.excludePaths; const normalizedExcludePaths = normalizeExcludePaths(excludePaths); @@ -130,4 +137,5 @@ module.exports = { writeRequires, getMain, getPreviewExists, + getFilePathExtension, }; diff --git a/app/react-native/scripts/loader.test.js b/app/react-native/scripts/loader.test.js index 3bdeec68e7..94e1cec487 100644 --- a/app/react-native/scripts/loader.test.js +++ b/app/react-native/scripts/loader.test.js @@ -46,6 +46,20 @@ describe('loader', () => { ], }); }); + it('should work for any supported file extension', () => { + const main = getMain({ configPath: './scripts/mocks/file-extensions' }); + expect(main).toEqual({ + default: { + stories: ['./FakeStory.stories.tsx'], + addons: [ + '@storybook/addon-ondevice-notes', + '@storybook/addon-ondevice-controls', + '@storybook/addon-ondevice-backgrounds', + '@storybook/addon-ondevice-actions', + ], + }, + }); + }); }); describe('getPreviewExists', () => { @@ -104,6 +118,16 @@ describe('loader', () => { }); }); + describe('when there are different file extensions', () => { + it('writes the story imports', () => { + writeRequires({ configPath: 'scripts/mocks/file-extensions' }); + expect(pathMock).toEqual( + path.resolve(__dirname, 'mocks/file-extensions/storybook.requires.js') + ); + expect(fileContentMock).toMatchSnapshot(); + }); + }); + describe('when there is a story glob and exclude paths globs', () => { it('writes the story imports', () => { writeRequires({ configPath: 'scripts/mocks/exclude-config-files' }); diff --git a/app/react-native/scripts/mocks/file-extensions/FakeComponent.tsx b/app/react-native/scripts/mocks/file-extensions/FakeComponent.tsx new file mode 100644 index 0000000000..23915b0493 --- /dev/null +++ b/app/react-native/scripts/mocks/file-extensions/FakeComponent.tsx @@ -0,0 +1 @@ +export const FakeComponent = () => null; diff --git a/app/react-native/scripts/mocks/file-extensions/FakeStory.stories.tsx b/app/react-native/scripts/mocks/file-extensions/FakeStory.stories.tsx new file mode 100644 index 0000000000..ca6d412600 --- /dev/null +++ b/app/react-native/scripts/mocks/file-extensions/FakeStory.stories.tsx @@ -0,0 +1,10 @@ +import { FakeComponent } from './FakeComponent'; + +export default { + title: 'components/FakeComponent', + component: FakeComponent, +}; + +export const Basic = { + args: {}, +}; diff --git a/app/react-native/scripts/mocks/file-extensions/main.ts b/app/react-native/scripts/mocks/file-extensions/main.ts new file mode 100644 index 0000000000..6bb3695fbf --- /dev/null +++ b/app/react-native/scripts/mocks/file-extensions/main.ts @@ -0,0 +1,13 @@ +import type { StorybookConfig } from '@storybook/core-common'; + +const config: StorybookConfig = { + stories: ['./FakeStory.stories.tsx'], + addons: [ + '@storybook/addon-ondevice-notes', + '@storybook/addon-ondevice-controls', + '@storybook/addon-ondevice-backgrounds', + '@storybook/addon-ondevice-actions', + ], +}; + +export default config; diff --git a/app/react-native/scripts/mocks/file-extensions/preview.tsx b/app/react-native/scripts/mocks/file-extensions/preview.tsx new file mode 100644 index 0000000000..e0d38bd536 --- /dev/null +++ b/app/react-native/scripts/mocks/file-extensions/preview.tsx @@ -0,0 +1,23 @@ +import { View, StyleSheet } from 'react-native'; +import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; + +export const decorators = [ + (StoryFn) => ( + + + + ), + withBackgrounds, +]; +export const parameters = { + my_param: 'anything', + backgrounds: [ + { name: 'plain', value: 'white', default: true }, + { name: 'warm', value: 'hotpink' }, + { name: 'cool', value: 'deepskyblue' }, + ], +}; + +const styles = StyleSheet.create({ + container: { padding: 8, flex: 1 }, +}); diff --git a/app/react-native/scripts/watcher.js b/app/react-native/scripts/watcher.js index 64d7febc79..5063ebb6f6 100644 --- a/app/react-native/scripts/watcher.js +++ b/app/react-native/scripts/watcher.js @@ -1,17 +1,20 @@ const chokidar = require('chokidar'); const path = require('path'); -const { writeRequires, getMain, getPreviewExists } = require('./loader'); +const { writeRequires, getMain, getFilePathExtension } = require('./loader'); const { getArguments } = require('./handle-args'); const args = getArguments(); const log = console.log.bind(console); -const watchPaths = ['./main.js']; +const mainExt = getFilePathExtension(args, 'main'); +const previewExt = getFilePathExtension(args, 'preview'); -if (getPreviewExists(args)) { - watchPaths.push('./preview.js'); +const watchPaths = [`./main.${mainExt}`]; + +if (previewExt) { + watchPaths.push(`./preview.${previewExt}`); } const updateRequires = (event, watchPath) => {