├── .nvmrc ├── docs ├── static │ ├── .nojekyll │ ├── robots.txt │ └── img │ │ ├── favicon.ico │ │ └── social-card.jpg ├── blog │ ├── tags.yml │ ├── authors.yml │ └── 2025-03-23-first-blog-post.md ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ └── styles.module.css │ ├── pages │ │ ├── index.module.css │ │ └── index.tsx │ └── css │ │ └── custom.css ├── tsconfig.json ├── .gitignore ├── README.md ├── sidebars.ts └── package.json ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── test.yml │ └── docs.yml ├── .gitattributes ├── packages ├── ondevice-actions │ ├── register.js │ ├── tsconfig.json │ ├── src │ │ ├── index.tsx │ │ └── components │ │ │ └── ActionLogger │ │ │ └── index.tsx │ ├── README.md │ └── package.json ├── ondevice-backgrounds │ ├── register.js │ ├── docs │ │ └── demo.gif │ ├── tsconfig.json │ ├── src │ │ ├── constants.ts │ │ ├── register.tsx │ │ ├── container.tsx │ │ ├── index.tsx │ │ └── Swatch.tsx │ ├── README.md │ └── package.json ├── ondevice-notes │ ├── register.js │ ├── docs │ │ └── demo.png │ ├── src │ │ ├── typings.d.ts │ │ ├── index.ts │ │ ├── ErrorBoundary.tsx │ │ └── register.tsx │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── README.md │ └── package.json ├── ondevice-controls │ ├── register.js │ ├── tsconfig.json │ ├── src │ │ ├── components │ │ │ ├── color-picker │ │ │ │ ├── resources │ │ │ │ │ ├── color-circle.png │ │ │ │ │ ├── color-circle.xcf │ │ │ │ │ └── hsv_triangle_mask.png │ │ │ │ └── index.ts │ │ │ ├── ModalPortal.tsx │ │ │ └── SliderWrapper.tsx │ │ ├── Panel.tsx │ │ ├── sharedTypes.ts │ │ ├── index.tsx │ │ ├── types │ │ │ ├── useResyncValue.ts │ │ │ ├── Boolean.tsx │ │ │ ├── index.ts │ │ │ ├── Text.tsx │ │ │ └── Array.tsx │ │ ├── PropField.tsx │ │ └── NoControlsWarning.tsx │ └── README.md ├── react-native │ ├── preview.js │ ├── preset.js │ ├── src │ │ ├── components │ │ │ └── StoryView │ │ │ │ ├── index.tsx │ │ │ │ └── ErrorBoundary.tsx │ │ ├── types │ │ │ └── index.ts │ │ ├── preview.ts │ │ ├── stub.tsx │ │ ├── hooks.tsx │ │ ├── polyfill.ts │ │ └── index.ts │ ├── bin │ │ └── get-stories.js │ ├── metro │ │ └── withStorybook.js │ ├── scripts │ │ ├── mocks │ │ │ ├── no-preview │ │ │ │ ├── FakeComponent.tsx │ │ │ │ ├── FakeStory.stories.tsx │ │ │ │ └── main.js │ │ │ ├── all-config-files │ │ │ │ ├── FakeComponent.tsx │ │ │ │ ├── FakeStory.stories.tsx │ │ │ │ ├── main.js │ │ │ │ └── preview.js │ │ │ ├── file-extensions │ │ │ │ ├── FakeComponent.tsx │ │ │ │ ├── FakeStory.stories.tsx │ │ │ │ ├── main.ts │ │ │ │ └── preview.tsx │ │ │ ├── blank-config │ │ │ │ └── main.js │ │ │ ├── wrong-extension-preview │ │ │ │ ├── FakeComponent.tsx │ │ │ │ ├── FakeStory.stories.tsx │ │ │ │ ├── main.js │ │ │ │ └── preview.txt │ │ │ ├── configuration-objects │ │ │ │ ├── components │ │ │ │ │ ├── FakeComponent.tsx │ │ │ │ │ └── FakeStory.stories.tsx │ │ │ │ ├── main.js │ │ │ │ └── preview.js │ │ │ ├── exclude-config-files │ │ │ │ ├── include-components │ │ │ │ │ ├── FakeComponent.tsx │ │ │ │ │ └── FakeStory.stories.tsx │ │ │ │ ├── exclude-components │ │ │ │ │ ├── FakeComponent.tsx │ │ │ │ │ └── FakeStory.stories.tsx │ │ │ │ ├── main.js │ │ │ │ └── preview.js │ │ │ └── preview-files │ │ │ │ ├── ts │ │ │ │ └── preview.ts │ │ │ │ ├── tsx │ │ │ │ └── preview.tsx │ │ │ │ ├── js │ │ │ │ └── preview.js │ │ │ │ └── jsx │ │ │ │ └── preview.jsx │ │ ├── get-stories.js │ │ └── handle-args.js │ ├── babel.config.js │ ├── template │ │ └── cli │ │ │ ├── stories │ │ │ ├── Page.stories.tsx │ │ │ ├── Header.stories.tsx │ │ │ └── Button.stories.tsx │ │ │ ├── main.ts │ │ │ ├── preview.tsx │ │ │ ├── index.ts │ │ │ └── storybook.requires.ts │ ├── tsconfig.json │ ├── setup.js │ ├── buildscripts │ │ └── gendtsdev.ts │ ├── tsup.config.ts │ └── jest.config.js ├── react-native-ui-common │ ├── src │ │ ├── util │ │ │ ├── index.ts │ │ │ └── useStyle.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useStoreState.ts │ │ │ └── useLastViewed.ts │ │ ├── assets │ │ │ └── react-native-logo.png │ │ ├── index.tsx │ │ ├── constants.ts │ │ ├── IconButton.tsx │ │ ├── StorageProvider.tsx │ │ └── LayoutProvider.tsx │ ├── tsconfig.json │ └── tsup.config.ts ├── react-native-theming │ ├── README.md │ ├── tsconfig.json │ ├── src │ │ ├── index.ts │ │ └── emotionAugmentation.d.ts │ ├── tsup.config.ts │ ├── scripts │ │ ├── gendtsdev.ts │ │ └── patchdts.ts │ └── package.json ├── react-native-ui │ ├── src │ │ ├── assets │ │ │ └── react-native-logo.png │ │ ├── index.tsx │ │ ├── constants.ts │ │ ├── icon │ │ │ ├── MenuIcon.tsx │ │ │ ├── GroupIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── ComponentIcon.tsx │ │ │ ├── StoryIcon.tsx │ │ │ ├── FaceHappyIcon.tsx │ │ │ ├── CloseIcon.tsx │ │ │ ├── CollapseAllIcon.tsx │ │ │ ├── ExpandAllIcon.tsx │ │ │ ├── BottomBarToggleIcon.tsx │ │ │ ├── CloseFullscreenIcon.tsx │ │ │ ├── FullscreenIcon.tsx │ │ │ └── CollapseIcon.tsx │ │ ├── decorators.tsx │ │ ├── Explorer.tsx │ │ └── Explorer.stories.tsx │ ├── tsconfig.json │ └── tsup.config.ts └── react-native-ui-lite │ ├── src │ ├── assets │ │ └── react-native-logo.png │ ├── index.tsx │ ├── constants.ts │ ├── useAnimatedValue.ts │ ├── Explorer.tsx │ └── Explorer.stories.tsx │ ├── tsconfig.json │ └── tsup.config.ts ├── .vscode └── settings.json ├── examples └── expo-example │ ├── .rnstorybook │ ├── local-addon-example │ │ ├── preview.js │ │ └── register.js │ ├── main.ts │ ├── preview.tsx │ └── index.tsx │ ├── assets │ ├── icon.png │ └── storybook.icon │ │ └── icon.json │ ├── App.tsx │ ├── index.js │ ├── .maestro │ └── baseline │ │ ├── TestCase---Basic.png │ │ ├── TestCase2---Basic.png │ │ ├── DeepControls---Basic.png │ │ ├── HiddenControls---Basic.png │ │ ├── InteractionExample---Static.png │ │ ├── ActionExample-Actions---Basic.png │ │ ├── ControlExamples-Array---Basic.png │ │ ├── ControlExamples-Boolean---On.png │ │ ├── ControlExamples-Date---Basic.png │ │ ├── ControlExamples-Number---Basic.png │ │ ├── ControlExamples-Number---Range.png │ │ ├── ControlExamples-Object---Basic.png │ │ ├── ControlExamples-Radio---Basic.png │ │ ├── ControlExamples-Select---Basic.png │ │ ├── ControlExamples-Text---Basic.png │ │ ├── InputExample-TextInput---Basic.png │ │ ├── InteractionExample---Scrolling.png │ │ ├── InteractionExample---Touchable.png │ │ ├── NotesExample---Notes-Example.png │ │ ├── ControlExamples-Boolean---Basic.png │ │ ├── LoginDocsExample-Button---Disabled.png │ │ ├── LoginDocsExample-Button---Loading.png │ │ ├── LoginDocsExample-Button---Primary.png │ │ ├── LoginDocsExample-Button---Secondary.png │ │ ├── LoginDocsExample-TextInput---Filled.png │ │ ├── NestingExample-MessageInput---Basic.png │ │ ├── react-native-ui-src-Layout---Basic.png │ │ ├── ControlExamples-Color---Color-Example.png │ │ ├── ControlExamples-Select---With-Labels.png │ │ ├── ControlExamples-Select---With-Mapping.png │ │ ├── LoginDocsExample-Button---Long-Title.png │ │ ├── LoginDocsExample-LoginForm---Default.png │ │ ├── LoginDocsExample-LoginForm---Loading.png │ │ ├── LoginDocsExample-TextInput---Default.png │ │ ├── LoginDocsExample-TextInput---Password.png │ │ ├── NestingExample-Message-bubble---First.png │ │ ├── BackgroundExample-BackgroundCsf---Basic.png │ │ ├── ControlExamples-ControlExample---Example.png │ │ ├── LoginDocsExample-LoginForm---Long-Errors.png │ │ ├── LoginDocsExample-LoginForm---With-Errors.png │ │ ├── LoginDocsExample-TextInput---Empty-State.png │ │ ├── LoginDocsExample-TextInput---Long-Label.png │ │ ├── LoginDocsExample-TextInput---Playground.png │ │ ├── LoginDocsExample-TextInput---With-Error.png │ │ ├── LoginDocsExample-TextInput---With-Label.png │ │ ├── SafeAreaExample-SafeAreaInside---Basic.png │ │ ├── SafeAreaExample-SafeAreaOutside---Basic.png │ │ ├── SafeAreaExample-UsableArea---Safe-Area.png │ │ ├── react-native-ui-UI-Sidebar-Tree---Dark.png │ │ ├── react-native-ui-UI-Sidebar-Tree---Full.png │ │ ├── ControlExamples-WebCompatibility---Defined.png │ │ ├── NestingExample-ChatMessage---Message-First.png │ │ ├── SafeAreaExample-UsableArea---No-Safe-Area.png │ │ ├── react-native-ui-UI-SearchResults---Default.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Empty.png │ │ ├── ControlExamples-WebCompatibility---Undefined.png │ │ ├── LoginDocsExample-Button---Secondary-Disabled.png │ │ ├── LoginDocsExample-Button---Secondary-Loading.png │ │ ├── LoginDocsExample-LoginForm---Email-Error-Only.png │ │ ├── NestingExample-ChatMessage---Message-Second.png │ │ ├── NestingExample-Message-bubble---Second-Story.png │ │ ├── SafeAreaExample-SafeAreaInside---List-Basic.png │ │ ├── SafeAreaExample-SafeAreaOutside---List-Basic.png │ │ ├── react-native-ui-UI-Sidebar-Explorer---Simple.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Loading.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Scrolled.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Simple.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Nested.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Types.png │ │ ├── react-native-ui-UI-StorybookLogo---Image-Logo.png │ │ ├── react-native-ui-UI-StorybookLogo---Title-Logo.png │ │ ├── LoginDocsExample-TextInput---Long-Error-Message.png │ │ ├── NestingExample-Message-Reactions---Message-One.png │ │ ├── NestingExample-Message-Reactions---Message-Two.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Searching.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---With-Refs.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Selection.png │ │ ├── LoginDocsExample-LoginForm---Password-Error-Only.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Index-Error.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Statuses-Open.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Expandable.png │ │ ├── react-native-ui-UI-StorybookLogo---Image-Url-Logo.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---With-Ref-Empty.png │ │ ├── react-native-ui-UI-StorybookLogo---Image-Source-Logo.png │ │ ├── react-native-ui-src-Layout---Overflow-Addons-Example.png │ │ ├── ControlExamples-Reproductions-SelectWithNumber---Basic.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Loading-With-Refs.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Statuses-Collapsed.png │ │ ├── react-native-ui-UI-StorybookLogo---Image-Element-Logo.png │ │ ├── react-native-ui-src-Layout---Overflow-Sidebar-Example.png │ │ ├── react-native-ui-UI-Sidebar-Tree---Single-Story-Components.png │ │ ├── react-native-ui-UI-Sidebar-Tree---Story-with-a-storyName.png │ │ ├── react-native-ui-UI-Sidebar-Sidebar---Loading-With-Ref-Error.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Expandable-Long-Name.png │ │ ├── react-native-ui-UI-Sidebar-TreeNode---Selection-With-Long-Name.png │ │ ├── NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---First.png │ │ └── NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---Second-Story.png │ ├── components │ ├── ControlExamples │ │ ├── Text │ │ │ ├── Text.tsx │ │ │ ├── Text.stories.tsx │ │ │ └── Text.test.tsx │ │ ├── Date │ │ │ ├── Date.tsx │ │ │ ├── Date.test.tsx │ │ │ └── Date.stories.tsx │ │ ├── Select │ │ │ ├── Select.tsx │ │ │ └── Select.test.tsx │ │ ├── Radio │ │ │ ├── Radio.tsx │ │ │ ├── Radio.test.tsx │ │ │ └── Radio.stories.tsx │ │ ├── Number │ │ │ ├── Number.tsx │ │ │ ├── Number.test.tsx │ │ │ └── Number.stories.tsx │ │ ├── Object │ │ │ ├── Object.tsx │ │ │ ├── Object.test.tsx │ │ │ └── Object.stories.tsx │ │ ├── Reproductions │ │ │ ├── SelectWithNumber.stories.tsx │ │ │ └── SelectWithNumber.tsx │ │ ├── Color │ │ │ ├── Color.test.tsx │ │ │ ├── Color.tsx │ │ │ └── Color.stories.tsx │ │ ├── Array │ │ │ ├── Array.test.tsx │ │ │ ├── Array.tsx │ │ │ └── Array.stories.tsx │ │ ├── Boolean │ │ │ ├── Boolean.stories.tsx │ │ │ ├── Boolean.test.tsx │ │ │ └── Boolean.tsx │ │ └── ControlExample │ │ │ └── ControlExample.tsx │ ├── InputExample │ │ ├── TextInput.test.tsx │ │ ├── TextInput.stories.tsx │ │ └── TextInput.tsx │ ├── NestingExample │ │ ├── ChatMessageMessageInput.stories.tsx │ │ ├── ChatMessage.stories.tsx │ │ ├── ChatMessageReactions.stories.tsx │ │ ├── ChatMessageBubble.stories.tsx │ │ └── ChatMessageBubbleAgain.stories.tsx │ ├── SafeAreaExample │ │ ├── AButton.tsx │ │ ├── UsableArea.stories.tsx │ │ └── SafeAreaInside.stories.tsx │ ├── BackgroundExample │ │ ├── BackgroundCsf.test.tsx │ │ └── BackgroundCsf.stories.tsx │ ├── ActionExample │ │ ├── Actions.test.tsx │ │ ├── Actions.stories.tsx │ │ └── Actions.tsx │ ├── StoryName │ │ └── StoryName.stories.tsx │ ├── HiddenControls │ │ └── HiddenControls.stories.tsx │ └── LoginDocsExample │ │ ├── LoginForm │ │ ├── LoginForm.stories.tsx │ │ └── LoginForm.tsx │ │ ├── Button │ │ ├── Button.stories.tsx │ │ └── Button.tsx │ │ └── TextInput │ │ └── TextInput.tsx │ ├── README.md │ ├── tsconfig.json │ ├── setup-jest.ts │ ├── babel.config.js │ ├── .storybook │ ├── preview.tsx │ └── main.ts │ ├── other_components │ ├── TestCase2 │ │ └── TestCase2.stories.tsx │ └── TestCase │ │ └── TestCase.stories.tsx │ ├── jest.config.js │ ├── eas.json │ ├── app.json │ ├── .gitignore │ └── metro.config.js ├── lerna.json ├── .editorconfig ├── .prettierrc ├── .yarnrc.yml ├── ROADMAP.md ├── .prettierignore ├── tsconfig.json ├── .gitignore ├── LICENSE └── eslint.config.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 24.11.1 -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dannyhw 2 | -------------------------------------------------------------------------------- /docs/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .yarn/releases/yarn-*.js linguist-generated=true 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: storybook 2 | github: dannyhw 3 | -------------------------------------------------------------------------------- /packages/ondevice-actions/register.js: -------------------------------------------------------------------------------- 1 | require('./dist').register(); 2 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/register.js: -------------------------------------------------------------------------------- 1 | require('./dist/register'); 2 | -------------------------------------------------------------------------------- /packages/ondevice-notes/register.js: -------------------------------------------------------------------------------- 1 | require('./dist/register.js'); 2 | -------------------------------------------------------------------------------- /packages/ondevice-controls/register.js: -------------------------------------------------------------------------------- 1 | require('./dist/index').register(); 2 | -------------------------------------------------------------------------------- /packages/react-native/preview.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/preview.js'); 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-native/preset.js: -------------------------------------------------------------------------------- 1 | // just to be a valid framework 2 | module.exports = {}; 3 | -------------------------------------------------------------------------------- /packages/react-native/src/components/StoryView/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './StoryView'; 2 | -------------------------------------------------------------------------------- /examples/expo-example/.rnstorybook/local-addon-example/preview.js: -------------------------------------------------------------------------------- 1 | console.log('preview-local-addon'); 2 | -------------------------------------------------------------------------------- /examples/expo-example/.rnstorybook/local-addon-example/register.js: -------------------------------------------------------------------------------- 1 | console.log('register-local-addon'); 2 | -------------------------------------------------------------------------------- /packages/react-native/bin/get-stories.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../scripts/get-stories.js'); 3 | -------------------------------------------------------------------------------- /packages/react-native/metro/withStorybook.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../dist/metro/withStorybook.js'); 2 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/no-preview/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/all-config-files/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/file-extensions/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /docs/static/img/social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/docs/static/img/social-card.jpg -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "registry": "https://registry.npmjs.org", 4 | "version": "10.1.1" 5 | } 6 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/blank-config/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [], 3 | addons: [], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/wrong-extension-preview/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/configuration-objects/components/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /examples/expo-example/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/assets/icon.png -------------------------------------------------------------------------------- /packages/ondevice-notes/docs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/ondevice-notes/docs/demo.png -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/include-components/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponent = () => null; 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | 6 | [*.{js,json,ts,vue,html}] 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/util/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StoryHash'; 2 | export * from './tree'; 3 | export * from './useStyle'; 4 | -------------------------------------------------------------------------------- /examples/expo-example/App.tsx: -------------------------------------------------------------------------------- 1 | // fixes fast refresh on web 2 | import '@expo/metro-runtime'; 3 | 4 | export { default } from './.rnstorybook'; 5 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/docs/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/ondevice-backgrounds/docs/demo.gif -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/exclude-components/FakeComponent.tsx: -------------------------------------------------------------------------------- 1 | export const FakeComponentExcluded = () => null; 2 | -------------------------------------------------------------------------------- /examples/expo-example/index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo'; 2 | 3 | import App from './App'; 4 | 5 | registerRootComponent(App); 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "bracketSpacing": true, 5 | "trailingComma": "es5", 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-4.9.4.cjs 8 | -------------------------------------------------------------------------------- /packages/ondevice-notes/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-native-simple-markdown' { 2 | const Markdown: any; 3 | export default Markdown; 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useExpanded'; 2 | export * from './useLastViewed'; 3 | export * from './useStoreState'; 4 | -------------------------------------------------------------------------------- /packages/react-native/babel.config.js: -------------------------------------------------------------------------------- 1 | // note this file is used by jest to make it not shit itself 2 | module.exports = { 3 | presets: ['babel-preset-expo'], 4 | }; 5 | -------------------------------------------------------------------------------- /packages/react-native-theming/README.md: -------------------------------------------------------------------------------- 1 | # @storybook/react-native-theming 2 | 3 | A wrapper library around emotion 11 to provide theming support for react-native storybook. 4 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/assets/react-native-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/react-native-ui/src/assets/react-native-logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/TestCase---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/TestCase---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/TestCase2---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/TestCase2---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/DeepControls---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/DeepControls---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Text/Text.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'react-native'; 2 | 3 | export const Heading = ({ text }: { text: string }) => {text}; 4 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/assets/react-native-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/react-native-ui-common/src/assets/react-native-logo.png -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/assets/react-native-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/react-native-ui-lite/src/assets/react-native-logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/HiddenControls---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/HiddenControls---Basic.png -------------------------------------------------------------------------------- /packages/react-native-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TreeNode'; 2 | export * from './Tree'; 3 | export * from './Explorer'; 4 | export * from './Sidebar'; 5 | export * from './Layout'; 6 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TreeNode'; 2 | export * from './Tree'; 3 | export * from './Explorer'; 4 | export * from './Sidebar'; 5 | export * from './Layout'; 6 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | For v11: 4 | 5 | - backgrounds with globals 6 | - addons into core 7 | - make lite ui the default and remove dependencies from controls 8 | - simple docs implementation 9 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/InteractionExample---Static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/InteractionExample---Static.png -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | dist/ 3 | examples/expo-example/.expo 4 | examples/expo-example/.rnstorybook/storybook.requires.ts 5 | docs/.docusaurus 6 | docs/build 7 | .claude/ 8 | examples/expo-example/assets -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ActionExample-Actions---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ActionExample-Actions---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Array---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Array---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Boolean---On.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Boolean---On.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Date---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Date---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Number---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Number---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Number---Range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Number---Range.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Object---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Object---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Radio---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Radio---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Select---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Select---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Text---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Text---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/InputExample-TextInput---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/InputExample-TextInput---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/InteractionExample---Scrolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/InteractionExample---Scrolling.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/InteractionExample---Touchable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/InteractionExample---Touchable.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NotesExample---Notes-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NotesExample---Notes-Example.png -------------------------------------------------------------------------------- /packages/react-native/scripts/get-stories.js: -------------------------------------------------------------------------------- 1 | const { getArguments } = require('./handle-args'); 2 | const args = getArguments(); 3 | 4 | const { generate } = require('./generate'); 5 | generate(args); 6 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Boolean---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Boolean---Basic.png -------------------------------------------------------------------------------- /packages/ondevice-controls/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/ondevice-notes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "dist/" 6 | }, 7 | "include": ["src/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /docs/blog/tags.yml: -------------------------------------------------------------------------------- 1 | releases: 2 | label: Releases 3 | permalink: /releases 4 | description: Release posts 5 | 6 | guides: 7 | label: Guides 8 | permalink: /guides 9 | description: Guide posts 10 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Disabled.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Loading.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Primary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Primary.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Filled.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-MessageInput---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-MessageInput---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Color---Color-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Color---Color-Example.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Select---With-Labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Select---With-Labels.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Select---With-Mapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Select---With-Mapping.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Long-Title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Long-Title.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Default.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Loading.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Default.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Password.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-bubble---First.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-bubble---First.png -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/color-picker/resources/color-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/ondevice-controls/src/components/color-picker/resources/color-circle.png -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/color-picker/resources/color-circle.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/ondevice-controls/src/components/color-picker/resources/color-circle.xcf -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/BackgroundExample-BackgroundCsf---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/BackgroundExample-BackgroundCsf---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-ControlExample---Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-ControlExample---Example.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Long-Errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Long-Errors.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---With-Errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---With-Errors.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Empty-State.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Empty-State.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Long-Label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Long-Label.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Playground.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---With-Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---With-Error.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---With-Label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---With-Label.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaInside---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaInside---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaOutside---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaOutside---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-UsableArea---Safe-Area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-UsableArea---Safe-Area.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Dark.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Full.png -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/" 5 | }, 6 | "include": ["src/**/*"], 7 | "exclude": ["src/__tests__/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-WebCompatibility---Defined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-WebCompatibility---Defined.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-ChatMessage---Message-First.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-ChatMessage---Message-First.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-UsableArea---No-Safe-Area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-UsableArea---No-Safe-Area.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-SearchResults---Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-SearchResults---Default.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Empty.png -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/color-picker/resources/hsv_triangle_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/packages/ondevice-controls/src/components/color-picker/resources/hsv_triangle_mask.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-WebCompatibility---Undefined.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-WebCompatibility---Undefined.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary-Disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary-Disabled.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary-Loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-Button---Secondary-Loading.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Email-Error-Only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Email-Error-Only.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-ChatMessage---Message-Second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-ChatMessage---Message-Second.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-bubble---Second-Story.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-bubble---Second-Story.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaInside---List-Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaInside---List-Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaOutside---List-Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/SafeAreaExample-SafeAreaOutside---List-Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Explorer---Simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Explorer---Simple.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Scrolled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Scrolled.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Simple.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Nested.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Nested.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Types.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Title-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Title-Logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Long-Error-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-TextInput---Long-Error-Message.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-Reactions---Message-One.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-Reactions---Message-One.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-Reactions---Message-Two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-Reactions---Message-Two.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Searching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Searching.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---With-Refs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---With-Refs.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Selection.png -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 5rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Password-Error-Only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/LoginDocsExample-LoginForm---Password-Error-Only.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Index-Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Index-Error.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Statuses-Open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Statuses-Open.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Expandable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Expandable.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Url-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Url-Logo.png -------------------------------------------------------------------------------- /packages/ondevice-notes/src/index.ts: -------------------------------------------------------------------------------- 1 | import { logger } from 'storybook/internal/client-logger'; 2 | 3 | // @ts-ignore 4 | if (__DEV__) { 5 | logger.log("import '@storybook/addon-ondevice-notes/register' to register the notes addon"); 6 | } 7 | -------------------------------------------------------------------------------- /packages/react-native-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "./src", 6 | "outDir": "dist/" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---With-Ref-Empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---With-Ref-Empty.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Source-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Source-Logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Overflow-Addons-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Overflow-Addons-Example.png -------------------------------------------------------------------------------- /packages/react-native-ui-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "./src", 6 | "outDir": "dist/" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "./src", 6 | "outDir": "dist/" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/ControlExamples-Reproductions-SelectWithNumber---Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/ControlExamples-Reproductions-SelectWithNumber---Basic.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading-With-Refs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading-With-Refs.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Statuses-Collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Statuses-Collapsed.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Element-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-StorybookLogo---Image-Element-Logo.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Overflow-Sidebar-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-src-Layout---Overflow-Sidebar-Example.png -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Date/Date.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'react-native'; 2 | 3 | export interface DateProps { 4 | date: Date; 5 | } 6 | 7 | export const DateString = ({ date }: DateProps) => {date.toString()}; 8 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Single-Story-Components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Single-Story-Components.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Story-with-a-storyName.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Tree---Story-with-a-storyName.png -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Select/Select.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'react-native'; 2 | 3 | export interface Props { 4 | arrow: string; 5 | } 6 | 7 | export const SelectExample = ({ arrow }: Props) => Selected: {arrow}; 8 | -------------------------------------------------------------------------------- /packages/ondevice-actions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "dist/" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["src/__tests__/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | }, 7 | "exclude": [".docusaurus", "build"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading-With-Ref-Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-Sidebar---Loading-With-Ref-Error.png -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Expandable-Long-Name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Expandable-Long-Name.png -------------------------------------------------------------------------------- /packages/react-native-ui/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const BREAKPOINT = 1000; 2 | export const MEDIA_DESKTOP_BREAKPOINT = `@media (min-width: ${BREAKPOINT}px)`; 3 | export const MOBILE_TRANSITION_DURATION = 300; 4 | export const DEFAULT_REF_ID = 'storybook_internal'; 5 | -------------------------------------------------------------------------------- /docs/blog/authors.yml: -------------------------------------------------------------------------------- 1 | dannyhw: 2 | name: Danny Williams 3 | title: Senior Software Engineer 4 | url: https://github.com/dannyhw 5 | image_url: https://github.com/dannyhw.png 6 | page: true 7 | socials: 8 | x: Danny_H_W 9 | github: dannyhw 10 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Selection-With-Long-Name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/react-native-ui-UI-Sidebar-TreeNode---Selection-With-Long-Name.png -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './IconButton'; 2 | export * from './Button'; 3 | export * from './LayoutProvider'; 4 | export * from './types'; 5 | export * from './StorageProvider'; 6 | export * from './util'; 7 | export * from './hooks'; 8 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const BREAKPOINT = 1000; 2 | export const MEDIA_DESKTOP_BREAKPOINT = `@media (min-width: ${BREAKPOINT}px)`; 3 | export const MOBILE_TRANSITION_DURATION = 300; 4 | export const DEFAULT_REF_ID = 'storybook_internal'; 5 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/configuration-objects/components/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | component: FakeComponent, 5 | }; 6 | 7 | export const Basic = { 8 | args: {}, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const BREAKPOINT = 1000; 2 | export const MEDIA_DESKTOP_BREAKPOINT = `@media (min-width: ${BREAKPOINT}px)`; 3 | export const MOBILE_TRANSITION_DURATION = 300; 4 | export const DEFAULT_REF_ID = 'storybook_internal'; 5 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/Panel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const AddonPanel = ({ active, children }) => { 4 | if (!active) { 5 | return null; 6 | } 7 | return <>{children}>; 8 | }; 9 | 10 | AddonPanel.displayName = 'AddonPanel'; 11 | -------------------------------------------------------------------------------- /examples/expo-example/README.md: -------------------------------------------------------------------------------- 1 | Try this: 2 | `npx uri-scheme open "exp://127.0.0.1:8081/--/?STORYBOOK_STORY_ID=controlexamples-boolean--basic" --ios` 3 | or this: 4 | `npx uri-scheme open "exp://127.0.0.1:8081/--/?STORYBOOK_STORY_ID=controlexamples-controlexample--example" --ios` 5 | -------------------------------------------------------------------------------- /packages/react-native-theming/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": "./src", 6 | "outDir": "dist/" 7 | }, 8 | "exclude": ["src/__tests__/**/*"], 9 | "include": ["src/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/no-preview/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponent', 5 | component: FakeComponent, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/file-extensions/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponent', 5 | component: FakeComponent, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Radio/Radio.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'react-native'; 2 | 3 | export interface RadioProps { 4 | selection: string; 5 | } 6 | 7 | export const Radio = ({ selection = '' }: RadioProps) => { 8 | return {selection}; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/sharedTypes.ts: -------------------------------------------------------------------------------- 1 | export type ControlTypes = 2 | | 'text' 3 | | 'number' 4 | | 'color' 5 | | 'boolean' 6 | | 'object' 7 | | 'select' 8 | | 'array' 9 | | 'date' 10 | | 'radio' 11 | | 'inline-radio' 12 | | 'multi-select' 13 | | 'range'; 14 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/all-config-files/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponent', 5 | component: FakeComponent, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/stories/Page.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta } from '@storybook/react'; 2 | 3 | import { Page } from './Page'; 4 | 5 | export default { 6 | title: 'Example/Page', 7 | component: Page, 8 | } as Meta; 9 | 10 | export const Default = {}; 11 | -------------------------------------------------------------------------------- /packages/react-native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDirs": ["./src", "./scripts"], 6 | "outDir": "dist/" 7 | }, 8 | "exclude": ["src/__tests__/**/*"], 9 | "include": ["src/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/wrong-extension-preview/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponent', 5 | component: FakeComponent, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/react-native/setup.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | jest.doMock('react-native/Libraries/NativeComponent/ViewConfigIgnore', () => ({ 3 | ConditionallyIgnoredEventHandlers: () => undefined, 4 | DynamicallyInjectedByGestureHandler: () => ({}), 5 | isIgnored: () => true, 6 | })); 7 | -------------------------------------------------------------------------------- /examples/expo-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "skipLibCheck": true, 4 | "baseUrl": "./", 5 | "strict": true, 6 | "esModuleInterop": true 7 | }, 8 | "extends": "expo/tsconfig.base", 9 | "include": [".rnstorybook/**/*", ".storybook/**/*", "./*"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/include-components/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponent } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponent', 5 | component: FakeComponent, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/expo-example/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'react-native-gesture-handler/jestSetup'; 2 | 3 | jest.doMock('react-native/Libraries/NativeComponent/ViewConfigIgnore', () => ({ 4 | ConditionallyIgnoredEventHandlers: () => undefined, 5 | DynamicallyInjectedByGestureHandler: () => ({}), 6 | isIgnored: () => true, 7 | })); 8 | -------------------------------------------------------------------------------- /packages/react-native/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { LoaderFunction } from 'storybook/internal/types'; 2 | 3 | export interface RequireContext { 4 | keys: () => string[]; 5 | (id: string): any; 6 | resolve(id: string): string; 7 | } 8 | 9 | export type Loadable = RequireContext | RequireContext[] | LoaderFunction; 10 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-native'; 2 | 3 | const main: StorybookConfig = { 4 | stories: ['./stories/**/*.stories.?(ts|tsx|js|jsx)'], 5 | addons: ['@storybook/addon-ondevice-controls', '@storybook/addon-ondevice-actions'], 6 | }; 7 | 8 | export default main; 9 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---First.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---First.png -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/exclude-components/FakeStory.stories.tsx: -------------------------------------------------------------------------------- 1 | import { FakeComponentExcluded } from './FakeComponent'; 2 | 3 | export default { 4 | title: 'components/FakeComponentExcluded', 5 | component: FakeComponentExcluded, 6 | }; 7 | 8 | export const Basic = { 9 | args: {}, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/no-preview/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['./FakeStory.stories.tsx'], 3 | addons: [ 4 | '@storybook/addon-ondevice-notes', 5 | '@storybook/addon-ondevice-controls', 6 | '@storybook/addon-ondevice-backgrounds', 7 | '@storybook/addon-ondevice-actions', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/all-config-files/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['./FakeStory.stories.tsx'], 3 | addons: [ 4 | '@storybook/addon-ondevice-notes', 5 | '@storybook/addon-ondevice-controls', 6 | '@storybook/addon-ondevice-backgrounds', 7 | '@storybook/addon-ondevice-actions', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /examples/expo-example/.maestro/baseline/NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---Second-Story.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybookjs/react-native/HEAD/examples/expo-example/.maestro/baseline/NestingExample-Message-bubble-a-very-long-name-for-a-title-that-just-keeps-going-and-going---Second-Story.png -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const PARAM_KEY = 'backgrounds'; 2 | export const ADDON_ID = 'storybook-addon-background'; 3 | export const PANEL_ID = `${ADDON_ID}/background-panel`; 4 | 5 | export default { 6 | SET: `${ADDON_ID}:set`, 7 | UNSET: `${ADDON_ID}:unset`, 8 | UPDATE_BACKGROUND: `${ADDON_ID}:update`, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/preview-files/ts/preview.ts: -------------------------------------------------------------------------------- 1 | export const decorators: any = []; 2 | export const parameters = { 3 | my_param: 'anything', 4 | backgrounds: [ 5 | { name: 'plain', value: 'white', default: true }, 6 | { name: 'warm', value: 'hotpink' }, 7 | { name: 'cool', value: 'deepskyblue' }, 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/preview-files/tsx/preview.tsx: -------------------------------------------------------------------------------- 1 | export const decorators: any = []; 2 | export const parameters = { 3 | my_param: 'anything', 4 | backgrounds: [ 5 | { name: 'plain', value: 'white', default: true }, 6 | { name: 'warm', value: 'hotpink' }, 7 | { name: 'cool', value: 'deepskyblue' }, 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/wrong-extension-preview/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['./FakeStory.stories.tsx'], 3 | addons: [ 4 | '@storybook/addon-ondevice-notes', 5 | '@storybook/addon-ondevice-controls', 6 | '@storybook/addon-ondevice-backgrounds', 7 | '@storybook/addon-ondevice-actions', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/preview.tsx: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@storybook/react-native'; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | }, 12 | }; 13 | 14 | export default preview; 15 | -------------------------------------------------------------------------------- /examples/expo-example/babel.config.js: -------------------------------------------------------------------------------- 1 | // const path = require('path'); 2 | 3 | module.exports = function (api) { 4 | api.cache(true); 5 | return { 6 | presets: [['babel-preset-expo']], 7 | plugins: [ 8 | ['babel-plugin-react-docgen-typescript', { exclude: 'node_modules' }], 9 | 'react-native-worklets/plugin', 10 | ], 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/file-extensions/main.ts: -------------------------------------------------------------------------------- 1 | const config = { 2 | stories: ['./FakeStory.stories.tsx'], 3 | addons: [ 4 | '@storybook/addon-ondevice-notes', 5 | '@storybook/addon-ondevice-controls', 6 | '@storybook/addon-ondevice-backgrounds', 7 | '@storybook/addon-ondevice-actions', 8 | ], 9 | }; 10 | 11 | export default config; 12 | -------------------------------------------------------------------------------- /packages/react-native-ui/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.tsx'], 6 | clean: !options.watch, 7 | dts: !options.watch 8 | ? { 9 | entry: ['src/index.tsx'], 10 | resolve: true, 11 | } 12 | : false, 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/index.ts: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | import { view } from './storybook.requires'; 3 | 4 | const StorybookUIRoot = view.getStorybookUI({ 5 | storage: { 6 | getItem: AsyncStorage.getItem, 7 | setItem: AsyncStorage.setItem, 8 | }, 9 | }); 10 | 11 | export default StorybookUIRoot; 12 | -------------------------------------------------------------------------------- /packages/react-native-theming/src/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import styled, { type StyledComponent } from '@emotion/native'; 3 | import { useTheme, withTheme, ThemeProvider } from '@emotion/react'; 4 | 5 | export { theme, darkTheme, StorybookTheme } from './theme'; 6 | 7 | export { styled, useTheme, withTheme, ThemeProvider, StyledComponent }; 8 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.tsx'], 6 | clean: !options.watch, 7 | dts: !options.watch 8 | ? { 9 | entry: ['src/index.tsx'], 10 | resolve: true, 11 | } 12 | : false, 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /examples/expo-example/.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | /** @type { import('@storybook/react').Preview } */ 2 | const preview = { 3 | parameters: { 4 | // actions: { argTypesRegex: '^on[A-Z].*' }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/i, 9 | }, 10 | }, 11 | }, 12 | }; 13 | 14 | export default preview; 15 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.tsx'], 6 | clean: !options.watch, 7 | dts: !options.watch 8 | ? { 9 | entry: ['src/index.tsx'], 10 | resolve: true, 11 | } 12 | : false, 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Issue: 2 | 3 | ## What I did 4 | 5 | ## How to test 6 | 7 | Please explain how to test your changes and consider the following questions 8 | 9 | - Does this need a new example in examples/expo-example? 10 | - Does this need an update to the documentation? 11 | 12 | If your answer is yes to any of these, please make sure to include it in your PR. 13 | -------------------------------------------------------------------------------- /packages/react-native-theming/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.ts'], 6 | // minify: !options.watch, 7 | clean: !options.watch, 8 | dts: !options.watch 9 | ? { 10 | entry: ['src/index.ts'], 11 | resolve: true, 12 | } 13 | : false, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Radio/Radio.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as RadioStories from './Radio.stories'; 4 | 5 | const { Basic } = composeStories(RadioStories); 6 | 7 | test('radio story renders', () => { 8 | render(); 9 | 10 | screen.getByText('104.8MHz'); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Text/Text.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Heading } from './Text'; 3 | 4 | const meta = { 5 | component: Heading, 6 | args: { text: 'Hello world!' }, 7 | } satisfies Meta; 8 | 9 | export default meta; 10 | 11 | type Story = StoryObj; 12 | 13 | export const Basic: Story = {}; 14 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Text/Text.test.tsx: -------------------------------------------------------------------------------- 1 | import { screen, render } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as TextStories from './Text.stories'; 4 | 5 | const { Basic } = composeStories(TextStories); 6 | 7 | test('text story renders', () => { 8 | render(); 9 | 10 | screen.getByText('Hello world!'); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps } from './Button'; 2 | import { forwardRef } from 'react'; 3 | 4 | export const IconButton = forwardRef( 5 | ({ padding = 'small', variant = 'ghost', ...props }: ButtonProps, ref) => { 6 | return ; 7 | } 8 | ); 9 | 10 | IconButton.displayName = 'IconButton'; 11 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['**/*.stories.tsx'], 3 | reactNativeOptions: { 4 | excludePaths: '**/exclude-components/**', 5 | }, 6 | addons: [ 7 | '@storybook/addon-ondevice-notes', 8 | '@storybook/addon-ondevice-controls', 9 | '@storybook/addon-ondevice-backgrounds', 10 | '@storybook/addon-ondevice-actions', 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /packages/react-native/src/preview.ts: -------------------------------------------------------------------------------- 1 | import { 2 | parameters as reactParameters, 3 | argTypesEnhancers, 4 | } from '@storybook/react/entry-preview-docs'; 5 | import { type Preview } from '@storybook/react'; 6 | 7 | const preview: Preview = { 8 | argTypesEnhancers, 9 | parameters: { 10 | docs: { 11 | extractArgTypes: reactParameters.docs.extractArgTypes, 12 | }, 13 | }, 14 | }; 15 | 16 | export default preview; 17 | -------------------------------------------------------------------------------- /examples/expo-example/components/InputExample/TextInput.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as InputStories from './TextInput.stories'; 4 | 5 | const { Basic } = composeStories(InputStories); 6 | 7 | test('text input story renders', () => { 8 | render(); 9 | 10 | screen.getByPlaceholderText('Type something'); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/react-native/buildscripts/gendtsdev.ts: -------------------------------------------------------------------------------- 1 | async function run() { 2 | const { writeFile } = await import('fs/promises'); 3 | // add a string of text at the end of the dist/index.d.ts file 4 | console.log('creating dev dist/index.d.ts'); 5 | const contents = `export * from '../src/index';`; 6 | await writeFile('dist/index.d.ts', contents); 7 | } 8 | 9 | run().catch((e) => { 10 | console.error(e); 11 | process.exit(1); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Number/Number.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text } from 'react-native'; 2 | 3 | export interface Props { 4 | first: number; 5 | second: number; 6 | } 7 | 8 | const styles = StyleSheet.create({ 9 | text: { fontSize: 18 }, 10 | }); 11 | 12 | export const Multiply = ({ first, second }: Props) => ( 13 | 14 | {first} x {second} = {first * second} 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /packages/react-native-theming/src/emotionAugmentation.d.ts: -------------------------------------------------------------------------------- 1 | // this file is only actually used in development 2 | // for prod/dist bundles we are bundling Emotion into our package 3 | 4 | import '@emotion/react'; 5 | 6 | declare module '@emotion/react' { 7 | type StorybookTheme = import('./theme').StorybookTheme; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-empty-object-type 10 | export interface Theme extends StorybookTheme {} 11 | } 12 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Object/Object.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from 'react-native'; 2 | 3 | export interface Props { 4 | filmInfo: { title: string; releaseYear: number; genre: string }; 5 | } 6 | 7 | export const Movie = ({ filmInfo }: Props) => ( 8 | 9 | title: {filmInfo.title} 10 | release year: {filmInfo.releaseYear} 11 | genre: {filmInfo.genre} 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Date/Date.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as DateStories from './Date.stories'; 4 | 5 | const { Basic } = composeStories(DateStories); 6 | 7 | test('date story renders', () => { 8 | render(); 9 | 10 | const date = new Date(1983, 1, 25); 11 | 12 | screen.getByText(date.toString()); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/useAnimatedValue.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | import { Animated } from 'react-native'; 3 | 4 | export default function useAnimatedValue( 5 | initialValue: number, 6 | config?: Animated.AnimatedConfig 7 | ): Animated.Value { 8 | const ref = useRef(null); 9 | if (ref.current == null) { 10 | ref.current = new Animated.Value(initialValue, config); 11 | } 12 | return ref.current; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/configuration-objects/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [ 3 | { 4 | files: '**/*.stories.tsx', 5 | directory: './components', 6 | titlePrefix: 'ComponentsPrefix', 7 | }, 8 | ], 9 | addons: [ 10 | '@storybook/addon-ondevice-notes', 11 | '@storybook/addon-ondevice-controls', 12 | '@storybook/addon-ondevice-backgrounds', 13 | '@storybook/addon-ondevice-actions', 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Reproductions/SelectWithNumber.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react-native'; 2 | 3 | import { MyButton } from './SelectWithNumber'; 4 | 5 | const meta = { 6 | component: MyButton, 7 | } satisfies Meta; 8 | 9 | export default meta; 10 | 11 | type Story = StoryObj; 12 | 13 | export const Basic: Story = { 14 | args: { 15 | number: undefined, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/react-native/src/stub.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from 'react-native'; 2 | 3 | const Stub = () => ( 4 | 12 | 13 | Storybook is disabled in the withStorybook metro wrapper. 14 | 15 | 16 | ); 17 | 18 | export default Stub; 19 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Color/Color.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as Color from './Color.stories'; 4 | 5 | const { ColorExample } = composeStories(Color); 6 | 7 | test('color story renders', () => { 8 | render(); 9 | 10 | expect(screen.getByTestId('color-story-container')).toHaveStyle({ backgroundColor: '#a819b9' }); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Array/Array.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as ArrayStories from './Array.stories'; 4 | 5 | // @ts-expect-error 6 | const { Basic } = composeStories(ArrayStories); 7 | 8 | test('array story renders', () => { 9 | render(); 10 | 11 | expect(screen.getByTestId('array-story-container')).toHaveTextContent(/abc/); 12 | }); 13 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Boolean/Boolean.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Switch } from './Boolean'; 3 | 4 | const meta = { 5 | component: Switch, 6 | } satisfies Meta; 7 | 8 | export default meta; 9 | 10 | type BooleanStory = StoryObj; 11 | 12 | export const Basic: BooleanStory = { 13 | args: { 14 | on: false, 15 | }, 16 | }; 17 | 18 | export const On: BooleanStory = { args: { on: true } }; 19 | -------------------------------------------------------------------------------- /examples/expo-example/other_components/TestCase2/TestCase2.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const TestCase2 = () => { 5 | return Testing story globs and nested stories; 6 | }; 7 | 8 | const meta = { 9 | component: TestCase2, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | 14 | type Story = StoryObj; 15 | 16 | export const Basic: Story = { 17 | args: {}, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/react-native/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.ts', 'src/preview.ts', 'src/metro/withStorybook.ts', 'src/stub.tsx'], 6 | // minify: !options.watch, 7 | clean: !options.watch, 8 | dts: !options.watch 9 | ? { 10 | entry: ['src/index.ts', 'src/preview.ts', 'src/metro/withStorybook.ts'], 11 | resolve: true, 12 | } 13 | : false, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Object/Object.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as ObjectStory from './Object.stories'; 4 | 5 | const { Basic } = composeStories(ObjectStory); 6 | 7 | test('object story renders', () => { 8 | render(); 9 | 10 | screen.getByText('title: Blade Runner'); 11 | screen.getByText('genre: Sci Fi'); 12 | screen.getByText('release year: 1982'); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/ondevice-notes/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig((options) => { 4 | return { 5 | entry: ['src/index.ts', 'src/register.tsx'], 6 | clean: !options.watch, 7 | dts: !options.watch 8 | ? { 9 | entry: ['src/index.ts', 'src/register.tsx'], 10 | resolve: true, 11 | } 12 | : false, 13 | // needed to pre-bundle the markdown package 14 | loader: { 15 | '.js': 'jsx', 16 | }, 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Color/Color.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native'; 2 | 3 | export interface ButtonProps { 4 | color: string; 5 | } 6 | 7 | const styles = StyleSheet.create({ 8 | button: { paddingHorizontal: 16, paddingVertical: 8 }, 9 | }); 10 | 11 | export const Color = ({ color }: ButtonProps) => ( 12 | 13 | Color: {color as string} 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Date/Date.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { DateString } from './Date'; 3 | 4 | const date = new Date(1983, 1, 25); 5 | 6 | const meta = { 7 | component: DateString, 8 | args: { date: date }, 9 | argTypes: { date: { control: { type: 'date' } } }, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | 14 | type DateStory = StoryObj; 15 | 16 | export const Basic: DateStory = {}; 17 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Object/Object.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Movie } from './Object'; 3 | 4 | const meta = { 5 | component: Movie, 6 | args: { 7 | filmInfo: { 8 | releaseYear: 1982, 9 | title: 'Blade Runner', 10 | genre: 'Sci Fi', 11 | }, 12 | }, 13 | } satisfies Meta; 14 | 15 | export default meta; 16 | 17 | type Story = StoryObj; 18 | 19 | export const Basic: Story = {}; 20 | -------------------------------------------------------------------------------- /packages/ondevice-actions/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { ADDON_ID, PANEL_ID, PARAM_KEY } from 'storybook/actions'; 2 | import { addons, types } from 'storybook/manager-api'; 3 | import ActionLogger from './containers/ActionLogger'; 4 | 5 | export function register() { 6 | addons.register(ADDON_ID, (_api) => { 7 | addons.add(PANEL_ID, { 8 | type: types.PANEL, 9 | title: 'Actions', 10 | render: ({ active }) => , 11 | paramKey: PARAM_KEY, 12 | }); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Array/Array.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View, StyleSheet } from 'react-native'; 2 | 3 | export interface ArrayProps { 4 | list: string[]; 5 | } 6 | 7 | export const Array = ({ list }: ArrayProps) => ( 8 | 9 | {list.map((item, index) => ( 10 | 11 | {item} 12 | 13 | ))} 14 | 15 | ); 16 | 17 | const styles = StyleSheet.create({ 18 | item: { padding: 8 }, 19 | }); 20 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/src/register.tsx: -------------------------------------------------------------------------------- 1 | import { addons, types } from 'storybook/manager-api'; 2 | import BackgroundPanel from './BackgroundPanel'; 3 | import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants'; 4 | 5 | addons.register(ADDON_ID, (api) => { 6 | const channel = addons.getChannel(); 7 | addons.add(PANEL_ID, { 8 | type: types.PANEL, 9 | title: 'Backgrounds', 10 | render: ({ active }) => , 11 | paramKey: PARAM_KEY, 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Boolean/Boolean.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as BooleanStories from './Boolean.stories'; 4 | 5 | const { Basic, On } = composeStories(BooleanStories); 6 | 7 | test('boolean story renders', () => { 8 | render(); 9 | 10 | screen.getByText('off'); 11 | }); 12 | 13 | test('boolean story renders on', () => { 14 | render(); 15 | 16 | screen.getByText('on'); 17 | }); 18 | -------------------------------------------------------------------------------- /examples/expo-example/components/InputExample/TextInput.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Input } from './TextInput'; 3 | 4 | const meta = { 5 | component: Input, 6 | parameters: { 7 | notes: 'Use this example to test the software keyboard related issues.', 8 | }, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | 13 | type Story = StoryObj; 14 | 15 | export const Basic: Story = { 16 | args: { 17 | placeholder: 'Type something', 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /examples/expo-example/components/NestingExample/ChatMessageMessageInput.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const MyComponent = ({ text }) => {text}; 5 | 6 | const meta = { 7 | title: 'NestingExample/MessageInput', 8 | component: MyComponent, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | 13 | type Story = StoryObj; 14 | 15 | export const Basic: Story = { 16 | args: { 17 | text: 'Hello', 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /examples/expo-example/jest.config.js: -------------------------------------------------------------------------------- 1 | // we use a config file because it resolves rootdir correctly 2 | 3 | /** @type {import('jest').Config} */ 4 | const config = { 5 | preset: 'jest-expo', 6 | setupFilesAfterEnv: ['/setup-jest.ts'], 7 | transformIgnorePatterns: [ 8 | 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|storybook/.*|@storybook/.*|uuid)', 9 | ], 10 | }; 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /packages/react-native/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('jest').Config} */ 2 | const config = { 3 | preset: 'jest-expo', 4 | setupFiles: ['/setup.js'], 5 | 6 | transformIgnorePatterns: [ 7 | 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|storybook/.*|@storybook/.*|uuid)', 8 | ], 9 | testPathIgnorePatterns: ['/node_modules/', '/scripts/generate\\.test\\.js$'], 10 | }; 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Color/Color.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Color } from './Color'; 3 | 4 | const meta = { 5 | parameters: { notes: '- test' }, 6 | argTypes: { 7 | color: { 8 | control: { type: 'color' }, 9 | }, 10 | }, 11 | component: Color, 12 | } satisfies Meta; 13 | 14 | export default meta; 15 | 16 | type ColorStory = StoryObj; 17 | 18 | export const ColorExample: ColorStory = { 19 | args: { 20 | color: '#a819b9', 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Number/Number.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as NumberStories from './Number.stories'; 4 | 5 | const { Basic, Range } = composeStories(NumberStories); 6 | 7 | test('basic story renders', async () => { 8 | render(); 9 | 10 | await screen.findByText(/5 x 3 = 15/); 11 | }); 12 | 13 | test('range story renders', async () => { 14 | render(); 15 | 16 | await screen.findByText(/6 x 7 = 42/); 17 | }); 18 | -------------------------------------------------------------------------------- /docs/blog/2025-03-23-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: hello-world 3 | title: Hello World 4 | authors: [dannyhw] 5 | tags: [] 6 | --- 7 | 8 | React Native Storybook docs are here. Over the next few weeks and months we'll be building out the documentation. 9 | 10 | 11 | 12 | As part of an effort to make React Native Storybook easier to use we've started working on dedicated documentation that will explain the nuances of running Storybook on React Native. 13 | 14 | If you have something you want to be documented help us build out the docs by submitting a pull request. 15 | -------------------------------------------------------------------------------- /packages/react-native-theming/scripts/gendtsdev.ts: -------------------------------------------------------------------------------- 1 | async function runFix() { 2 | const { writeFile } = await import('fs/promises'); 3 | // add a string of text at the end of the dist/index.d.ts file 4 | console.log('creating dev dist/index.d.ts'); 5 | const contents = ` 6 | import { StorybookTheme } from '../src/index'; 7 | export * from '../src/index'; 8 | export interface Theme extends StorybookTheme {} 9 | `; 10 | await writeFile('dist/index.d.ts', contents); 11 | } 12 | 13 | runFix().catch((e) => { 14 | console.error(e); 15 | process.exit(1); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/MenuIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | 3 | export const MenuIcon = ({ 4 | color = 'currentColor', 5 | width = 14, 6 | height = 14, 7 | ...props 8 | }: SvgProps) => { 9 | return ( 10 | 11 | 15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /examples/expo-example/other_components/TestCase/TestCase.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react-native'; 2 | import { useRef } from 'react'; 3 | import { Text } from 'react-native'; 4 | 5 | const TestCase = () => { 6 | const unstableRef = useRef(Math.random().toString(36).slice(2, 11)).current; 7 | return {unstableRef}; 8 | }; 9 | 10 | const meta = { 11 | component: TestCase, 12 | } satisfies Meta; 13 | 14 | export default meta; 15 | 16 | type Story = StoryObj; 17 | 18 | export const Basic: Story = { 19 | args: {}, 20 | }; 21 | -------------------------------------------------------------------------------- /examples/expo-example/components/SafeAreaExample/AButton.tsx: -------------------------------------------------------------------------------- 1 | import { TouchableOpacity, Text, StyleSheet } from 'react-native'; 2 | 3 | interface AButtonProps { 4 | onPress: () => void; 5 | text: string; 6 | } 7 | 8 | export const AButton = ({ onPress, text }: AButtonProps) => ( 9 | 10 | {text} 11 | 12 | ); 13 | 14 | const styles = StyleSheet.create({ 15 | container: { paddingHorizontal: 16, paddingVertical: 8, backgroundColor: 'violet' }, 16 | text: { color: 'black' }, 17 | }); 18 | -------------------------------------------------------------------------------- /examples/expo-example/components/NestingExample/ChatMessage.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const MyComponent = ({ text }) => {text}; 5 | 6 | const meta = { 7 | component: MyComponent, 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const MessageFirst: Story = { 15 | args: { 16 | text: 'Hello', 17 | }, 18 | }; 19 | 20 | export const MessageSecond: Story = { 21 | args: { 22 | text: 'Message two', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /examples/expo-example/components/BackgroundExample/BackgroundCsf.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as Backgrounds from './BackgroundCsf.stories'; 4 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 5 | 6 | const { Basic } = composeStories(Backgrounds, { decorators: [withBackgrounds] }); 7 | 8 | test('Background colour is hotpink', () => { 9 | render(); 10 | 11 | expect(screen.getByTestId('addon-backgrounds-container')).toHaveStyle({ 12 | backgroundColor: 'hotpink', 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Reproductions/SelectWithNumber.tsx: -------------------------------------------------------------------------------- 1 | import { Text, TouchableOpacity, View } from 'react-native'; 2 | 3 | export type ButtonProps = { 4 | number: keyof { 5 | 1: 'test'; 6 | 2: 'test'; 7 | 3: 'test'; 8 | 4: 'test'; 9 | 5: 'test'; 10 | 6: 'test'; 11 | 7: 'test'; 12 | 8: 'test'; 13 | 9: 'test'; 14 | }; 15 | }; 16 | 17 | export const MyButton = ({ number }: ButtonProps) => { 18 | return ( 19 | 20 | 21 | num: {number} 22 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Radio/Radio.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Radio } from './Radio'; 3 | 4 | const radio_stations = ['104.8MHz', '909 kHz', '90FM']; 5 | 6 | const meta = { 7 | component: Radio, 8 | 9 | argTypes: { 10 | selection: { 11 | options: radio_stations, 12 | control: { type: 'radio' }, 13 | }, 14 | }, 15 | } satisfies Meta; 16 | 17 | export default meta; 18 | 19 | type Story = StoryObj; 20 | 21 | export const Basic: Story = { 22 | args: { 23 | selection: radio_stations[0], 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react-native/src/hooks.tsx: -------------------------------------------------------------------------------- 1 | import type { Args, StoryContext } from 'storybook/internal/csf'; 2 | import { atom, useAtomValue, useSetAtom } from 'jotai'; 3 | import type { ReactRenderer } from '@storybook/react'; 4 | 5 | const storyContextAtom = atom(null as StoryContext | null); 6 | 7 | /** 8 | * Hook that returns a function to set the current story context. 9 | */ 10 | export function useSetStoryContext() { 11 | return useSetAtom(storyContextAtom); 12 | } 13 | 14 | /** 15 | * Hook to read the current story context. 16 | */ 17 | export function useStoryContext() { 18 | return useAtomValue(storyContextAtom); 19 | } 20 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/GroupIcon.tsx: -------------------------------------------------------------------------------- 1 | import Svg, { Path, SvgProps } from 'react-native-svg'; 2 | 3 | export function GroupIcon({ color = '#6F2CAC', ...props }: SvgProps) { 4 | return ( 5 | 6 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-native/src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from 'react-native-web'; 2 | 3 | if (Platform.OS !== 'web') { 4 | // We polyfill URLSearchParams for React Native since URLSearchParams.get is not implemented yet is used in storybook 5 | // with expo this would never run because its already polyfilled 6 | try { 7 | let params = new URLSearchParams({ test: '1' }); 8 | 9 | // the base react native url implementation throws an error when trying to access this function 10 | params.get('test'); 11 | } catch { 12 | const { setupURLPolyfill } = require('react-native-url-polyfill'); 13 | 14 | setupURLPolyfill(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/expo-example/components/NestingExample/ChatMessageReactions.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const MyComponent = ({ text }) => {text}; 5 | 6 | const meta = { 7 | title: 'NestingExample/Message/Reactions', 8 | component: MyComponent, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | 13 | type Story = StoryObj; 14 | 15 | export const MessageOne: Story = { 16 | args: { 17 | text: 'Hello', 18 | }, 19 | }; 20 | 21 | export const MessageTwo: Story = { 22 | args: { 23 | text: 'Message two', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /examples/expo-example/components/NestingExample/ChatMessageBubble.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const MyComponent = ({ text }) => {text}; 5 | 6 | const meta = { 7 | title: 'NestingExample/Message/bubble', 8 | component: MyComponent, 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | 13 | type Story = StoryObj; 14 | 15 | export const First: Story = { 16 | args: { 17 | text: 'First', 18 | }, 19 | }; 20 | 21 | export const Second: Story = { 22 | name: 'Second Story', 23 | args: { 24 | text: 'Second', 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/SearchIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | import { useTheme } from '@storybook/react-native-theming'; 3 | 4 | export const SearchIcon = ({ width = 14, height = 14, ...props }: SvgProps) => { 5 | const theme = useTheme(); 6 | return ( 7 | 8 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/expo-example/components/ActionExample/Actions.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, userEvent } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as Actions from './Actions.stories'; 4 | 5 | const { Basic } = composeStories(Actions); 6 | 7 | test('action story renders and onpress works', async () => { 8 | jest.useFakeTimers(); 9 | 10 | const onPress = jest.fn(); 11 | 12 | render(); 13 | 14 | const user = userEvent.setup({}); 15 | 16 | const actionButton = screen.getByText('Press me!'); 17 | 18 | await user.press(actionButton); 19 | 20 | expect(onPress).toHaveBeenCalled(); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { addons, types } from 'storybook/manager-api'; 2 | 3 | import ControlsPanel from './ControlsPanel'; 4 | import { AddonPanel } from './Panel'; 5 | 6 | export const ADDON_ID = 'RNCONTROLS' as const; 7 | export const PARAM_KEY = 'controls' as const; 8 | 9 | export function register() { 10 | addons.register(ADDON_ID, (api) => { 11 | addons.add(ADDON_ID, { 12 | type: types.PANEL, 13 | title: 'Controls', 14 | render: ({ active }) => ( 15 | 16 | 17 | 18 | ), 19 | paramKey: PARAM_KEY, 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Number/Number.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Multiply } from './Number'; 3 | 4 | const meta = { 5 | component: Multiply, 6 | } satisfies Meta; 7 | 8 | export default meta; 9 | 10 | type Story = StoryObj; 11 | 12 | export const Basic: Story = { 13 | args: { 14 | first: 5, 15 | second: 3, 16 | }, 17 | }; 18 | 19 | export const Range: Story = { 20 | args: { 21 | first: 6, 22 | second: 7, 23 | }, 24 | argTypes: { 25 | first: { 26 | step: 3, 27 | min: 1, 28 | max: 42, 29 | range: true, 30 | }, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/file-extensions/preview.tsx: -------------------------------------------------------------------------------- 1 | import { View, StyleSheet } from 'react-native'; 2 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 3 | 4 | export const decorators = [ 5 | (StoryFn) => ( 6 | 7 | 8 | 9 | ), 10 | withBackgrounds, 11 | ]; 12 | export const parameters = { 13 | my_param: 'anything', 14 | backgrounds: [ 15 | { name: 'plain', value: 'white', default: true }, 16 | { name: 'warm', value: 'hotpink' }, 17 | { name: 'cool', value: 'deepskyblue' }, 18 | ], 19 | }; 20 | 21 | const styles = StyleSheet.create({ 22 | container: { padding: 8, flex: 1 }, 23 | }); 24 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/ComponentIcon.tsx: -------------------------------------------------------------------------------- 1 | import Svg, { Path, SvgProps } from 'react-native-svg'; 2 | 3 | export function ComponentIcon({ color = '#029CFD', ...props }: SvgProps) { 4 | return ( 5 | 6 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/StoryIcon.tsx: -------------------------------------------------------------------------------- 1 | import Svg, { Path, SvgProps } from 'react-native-svg'; 2 | 3 | export function StoryIcon({ color = '#37D5D3', ...props }: SvgProps) { 4 | return ( 5 | 6 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /examples/expo-example/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-native-web-vite'; 2 | 3 | type ServerStorybookConfig = StorybookConfig & { 4 | reactNativeServerOptions: { host: string; port: number }; 5 | }; 6 | 7 | const main: ServerStorybookConfig = { 8 | stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'], 9 | 10 | addons: ['@storybook/addon-react-native-server', 'storybook-addon-deep-controls'], 11 | 12 | framework: { 13 | name: '@storybook/react-native-web-vite', 14 | options: {}, 15 | }, 16 | 17 | // logLevel: 'debug', 18 | 19 | reactNativeServerOptions: { 20 | host: 'localhost', 21 | port: 7007, 22 | }, 23 | }; 24 | 25 | export default main; 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "allowSyntheticDefaultImports": true, 6 | "baseUrl": ".", 7 | "declaration": true, 8 | "jsx": "react-jsx", 9 | "lib": ["es2022"], 10 | "module": "NodeNext", 11 | "moduleResolution": "NodeNext", 12 | "outDir": "dist/", 13 | "skipDefaultLibCheck": true, 14 | "skipLibCheck": true, 15 | "esModuleInterop": true, 16 | "target": "es2022", 17 | "resolveJsonModule": true 18 | }, 19 | "exclude": [ 20 | "**/dist", 21 | "node_modules", 22 | "**/node_modules", 23 | "**/*.spec.ts", 24 | "**/__tests__", 25 | "**/*.test.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/ondevice-actions/README.md: -------------------------------------------------------------------------------- 1 | # Storybook Actions Addon for react-native 2 | 3 | Storybook Actions Addon allows you to log events/actions inside stories in [Storybook](https://storybook.js.org). 4 | 5 | ## Installation 6 | 7 | ```sh 8 | yarn add -D @storybook/addon-ondevice-actions 9 | ``` 10 | 11 | ## Configuration 12 | 13 | Then, add following content to `.rnstorybook/main.ts`: 14 | 15 | ```ts 16 | import type { StorybookConfig } from '@storybook/react-native'; 17 | 18 | const main: StorybookConfig = { 19 | addons: ['@storybook/addon-ondevice-actions'], 20 | }; 21 | 22 | export default main; 23 | ``` 24 | 25 | The [actions documentation](https://storybook.js.org/docs/react/essentials/actions) may also be useful. 26 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/all-config-files/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/preview-files/js/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/preview-files/jsx/preview.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | 12 | **Describe alternatives you've considered** 13 | A clear and concise description of any alternative solutions or features you've considered. 14 | 15 | **Are you able to assist bring the feature to reality?** 16 | no | yes, I can... 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/configuration-objects/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/exclude-config-files/preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-native/scripts/mocks/wrong-extension-preview/preview.txt: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 4 | 5 | export const decorators = [ 6 | (StoryFn) => ( 7 | 8 | 9 | 10 | ), 11 | withBackgrounds, 12 | ]; 13 | export const parameters = { 14 | my_param: 'anything', 15 | backgrounds: [ 16 | { name: 'plain', value: 'white', default: true }, 17 | { name: 'warm', value: 'hotpink' }, 18 | { name: 'cool', value: 'deepskyblue' }, 19 | ], 20 | }; 21 | 22 | const styles = StyleSheet.create({ 23 | container: { padding: 8, flex: 1 }, 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/StorageProvider.tsx: -------------------------------------------------------------------------------- 1 | import type { FC, PropsWithChildren } from 'react'; 2 | import { createContext, useContext } from 'react'; 3 | 4 | export interface Storage { 5 | getItem: (key: string) => Promise; 6 | setItem: (key: string, value: string) => Promise; 7 | } 8 | 9 | const StorageContext = createContext({ 10 | getItem: async () => null, 11 | setItem: async () => {}, 12 | }); 13 | 14 | export const StorageProvider: FC> = ({ 15 | storage, 16 | children, 17 | }) => { 18 | return {children}; 19 | }; 20 | 21 | export const useStorage = () => useContext(StorageContext); 22 | -------------------------------------------------------------------------------- /examples/expo-example/components/NestingExample/ChatMessageBubbleAgain.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const MyComponent = ({ text }) => {text}; 5 | 6 | const meta = { 7 | title: 8 | 'NestingExample/Message/bubble/a very long name for a title that just keeps going and going', 9 | component: MyComponent, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | 14 | type Story = StoryObj; 15 | 16 | export const First: Story = { 17 | args: { 18 | text: 'First', 19 | }, 20 | }; 21 | 22 | export const Second: Story = { 23 | name: 'Second Story', 24 | args: { 25 | text: 'Second', 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/react-native/scripts/handle-args.js: -------------------------------------------------------------------------------- 1 | function getArguments() { 2 | const { program } = require('commander'); 3 | 4 | program 5 | .description('Generator for the storybook.requires file used in react native storybook') 6 | .option( 7 | '-c, --config-path ', 8 | 'The path to your config folder relative to your project-dir', 9 | './.rnstorybook' 10 | ) 11 | .option('-j, --use-js', 'Use a js file for storybook.requires') 12 | .option('-D, --no-doc-tools', 'Do not include doc tools in the storybook.requires file') 13 | .option('-a, --absolute', 'Use absolute paths for story imports'); 14 | 15 | program.parse(); 16 | 17 | return program.opts(); 18 | } 19 | 20 | module.exports = { getArguments }; 21 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Boolean/Boolean.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native'; 2 | 3 | export interface Props { 4 | on: boolean; 5 | } 6 | 7 | const styles = StyleSheet.create({ 8 | circle: { 9 | width: 60, 10 | height: 60, 11 | borderRadius: 30, 12 | justifyContent: 'center', 13 | alignItems: 'center', 14 | }, 15 | on: { backgroundColor: 'yellow' }, 16 | off: { backgroundColor: 'black' }, 17 | onText: { color: 'black' }, 18 | offText: { color: 'white' }, 19 | }); 20 | 21 | export const Switch = ({ on }: Props) => ( 22 | 23 | {on ? 'on' : 'off'} 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .idea 4 | *.iml 5 | *.sw* 6 | npm-shrinkwrap.json 7 | dist 8 | .tern-port 9 | *.DS_Store 10 | .cache 11 | junit.xml 12 | coverage/ 13 | *.lerna_backup 14 | build 15 | /**/LICENSE 16 | docs/public 17 | packs/*.tgz 18 | package-lock.json 19 | storybook-static 20 | integration/__image_snapshots__/__diff_output__ 21 | .jest-test-results.json 22 | lib/*.jar 23 | lib/**/dll 24 | .expo/packager-info.json 25 | scripts/storage 26 | htpasswd 27 | /false 28 | storybook-out 29 | /addons/docs/common/config-* 30 | built-storybooks 31 | .yarn/* 32 | !.yarn/patches 33 | !.yarn/plugins 34 | !.yarn/releases 35 | !.yarn/sdks 36 | !.yarn/versions 37 | .eslintcache 38 | examples/expo-example/.maestro/diffs 39 | examples/expo-example/.maestro/screenshots 40 | -------------------------------------------------------------------------------- /examples/expo-example/components/ActionExample/Actions.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { ActionButton } from './Actions'; 3 | import { fn } from 'storybook/test'; 4 | 5 | const meta = { 6 | component: ActionButton, 7 | parameters: { 8 | notes: ` 9 | # Button 10 | 11 | This is a button component. 12 | You use it like this: 13 | 14 | \`\`\`tsx 15 | console.log('pressed')} 18 | /> 19 | \`\`\` 20 | `, 21 | }, 22 | } satisfies Meta; 23 | 24 | export default meta; 25 | 26 | type Story = StoryObj; 27 | 28 | export const Basic: Story = { 29 | args: { 30 | text: 'Press me!', 31 | onPress: fn(), 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/types/useResyncValue.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | // syncs up the value of a control with the value of a story arg when resetting 4 | // this is used for controls that don't use a controlled input like the slider 5 | export function useResyncValue( 6 | value: any, 7 | isPristine: boolean, 8 | resyncCallback?: (syncValue: any) => void 9 | ) { 10 | const [key, setKey] = useState(0); 11 | const [currentValue, setCurrentValue] = useState(value); 12 | 13 | useEffect(() => { 14 | if (isPristine && value !== currentValue) { 15 | setKey((cur) => cur + 1); 16 | resyncCallback?.(value); 17 | } 18 | }, [value, currentValue, isPristine, resyncCallback]); 19 | return { key, setCurrentValue }; 20 | } 21 | -------------------------------------------------------------------------------- /examples/expo-example/components/ActionExample/Actions.tsx: -------------------------------------------------------------------------------- 1 | import { TouchableOpacity, Text, StyleSheet } from 'react-native'; 2 | 3 | export interface ActionButtonProps { 4 | onPress?: () => void; 5 | text: string; 6 | } 7 | 8 | export const ActionButton = ({ onPress, text }: ActionButtonProps) => { 9 | return ( 10 | 11 | {text} 12 | 13 | ); 14 | }; 15 | 16 | const styles = StyleSheet.create({ 17 | container: { 18 | paddingHorizontal: 32, 19 | paddingVertical: 16, 20 | backgroundColor: 'purple', 21 | borderRadius: 8, 22 | alignItems: 'center', 23 | }, 24 | text: { color: 'white', fontWeight: 'bold', fontSize: 18 }, 25 | }); 26 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Select/Select.test.tsx: -------------------------------------------------------------------------------- 1 | import { screen, render } from '@testing-library/react-native'; 2 | import { composeStories } from '@storybook/react'; 3 | import * as SelectStories from './Select.stories'; 4 | 5 | const { Basic, WithLabels, WithMapping } = composeStories(SelectStories); 6 | 7 | test('select story renders', () => { 8 | render(); 9 | 10 | screen.getByText('Selected: ⬅️'); 11 | }); 12 | 13 | test('select with labels story renders', () => { 14 | render(); 15 | 16 | screen.getByText('Selected: ⬆'); 17 | }); 18 | 19 | // TODO: Fix this test 20 | test.skip('select with mapping story renders', async () => { 21 | render(); 22 | 23 | await screen.findByText('Selected: ➡️'); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/types/Boolean.tsx: -------------------------------------------------------------------------------- 1 | import { Switch } from 'react-native'; 2 | import { styled } from '@storybook/react-native-theming'; 3 | 4 | export interface BooleanProps { 5 | onChange: (value: boolean) => void; 6 | arg: { 7 | name: string; 8 | value: boolean; 9 | }; 10 | } 11 | 12 | const Container = styled.View(() => ({ 13 | alignItems: 'flex-start', 14 | })); 15 | 16 | const BooleanType = ({ arg, onChange }: BooleanProps) => ( 17 | 18 | onChange(!arg.value)} value={arg.value} /> 19 | 20 | ); 21 | 22 | BooleanType.serialize = (value) => (value ? String(value) : null); 23 | 24 | BooleanType.deserialize = (value) => value === 'true'; 25 | 26 | export default BooleanType; 27 | -------------------------------------------------------------------------------- /packages/react-native-theming/scripts/patchdts.ts: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const { readFile, writeFile } = await import('fs/promises'); 3 | // add a string of text at the end of the dist/index.d.ts file 4 | console.log('writing to dist/index.d.ts'); 5 | const contents = await readFile('dist/index.d.ts', 'utf-8'); 6 | if (contents.includes('interface Theme {}')) { 7 | await writeFile( 8 | 'dist/index.d.ts', 9 | contents.replace('interface Theme {}', 'export interface Theme extends StorybookTheme {}') 10 | ); 11 | } else { 12 | await writeFile( 13 | 'dist/index.d.ts', 14 | `${contents}\nexport interface Theme extends StorybookTheme {}` 15 | ); 16 | } 17 | } 18 | 19 | main().catch((e) => { 20 | console.error(e); 21 | process.exit(1); 22 | }); 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - next 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | 10 | jobs: 11 | build: 12 | name: Check everything! 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set node version 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: 24 20 | - name: install and compile 21 | run: yarn 22 | - name: build 23 | run: yarn build 24 | - name: lint 25 | run: yarn lint 26 | - name: format check 27 | run: yarn format:check 28 | - name: type check example 29 | run: yarn workspace expo-example check 30 | - name: test 31 | run: yarn test 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **To Reproduce** 10 | Steps to reproduce the behavior: 11 | 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Code snippets** 24 | If applicable, add code samples to help explain your problem. 25 | 26 | **System:** 27 | Please paste the results of `npx storybook@latest info` here. 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/FaceHappyIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | 3 | export const FaceHappyIcon = ({ color, width = 14, height = 14, ...props }: SvgProps) => { 4 | return ( 5 | 6 | 10 | 16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /examples/expo-example/eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 16.19.3", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "developmentClient": true, 9 | "distribution": "internal", 10 | "channel": "development", 11 | "env": { 12 | "EXPO_PUBLIC_STORYBOOK_ENABLED": "true" 13 | } 14 | }, 15 | "preview": { 16 | "distribution": "internal", 17 | "channel": "preview", 18 | "env": { 19 | "EXPO_PUBLIC_STORYBOOK_ENABLED": "true" 20 | } 21 | }, 22 | "production": { 23 | "autoIncrement": true, 24 | "channel": "production", 25 | "env": { 26 | "EXPO_PUBLIC_STORYBOOK_ENABLED": "true" 27 | } 28 | } 29 | }, 30 | "submit": { 31 | "production": {} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/CloseIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | 3 | export const CloseIcon = ({ 4 | color = 'currentColor', 5 | width = 14, 6 | height = 14, 7 | ...props 8 | }: SvgProps) => { 9 | return ( 10 | 11 | 15 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import TextType from './Text'; 2 | import NumberType from './Number'; 3 | import ColorType from './Color'; 4 | import BooleanType from './Boolean'; 5 | import ObjectType from './Object'; 6 | import SelectType from './Select'; 7 | import DateType from './Date'; 8 | import ArrayType from './Array'; 9 | import RadioType from './Radio'; 10 | import { ControlTypes } from '../sharedTypes'; 11 | 12 | export default { 13 | text: TextType, 14 | number: NumberType, 15 | color: ColorType, 16 | boolean: BooleanType, 17 | object: ObjectType, 18 | select: SelectType, 19 | date: DateType, 20 | array: ArrayType, 21 | radio: RadioType, 22 | 'inline-radio': RadioType, 23 | 'multi-select': SelectType, 24 | range: NumberType, 25 | } satisfies Record>; 26 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/hooks/useStoreState.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useStorage } from '../StorageProvider'; 3 | 4 | export const useStoreBooleanState = ( 5 | key: string, 6 | defaultValue: boolean 7 | ): ReturnType> => { 8 | const storage = useStorage(); 9 | 10 | const [val, setVal] = useState(defaultValue); 11 | 12 | useEffect(() => { 13 | storage.getItem(key).then((newVal) => { 14 | if (newVal === null || newVal === undefined) { 15 | setVal(defaultValue); 16 | } else { 17 | setVal(newVal === 'true'); 18 | } 19 | }); 20 | }, [key, storage, defaultValue]); 21 | 22 | useEffect(() => { 23 | storage.setItem(key, val.toString()); 24 | }, [key, storage, val]); 25 | 26 | return [val, setVal]; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/decorators.tsx: -------------------------------------------------------------------------------- 1 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 2 | import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; 3 | import { LayoutProvider } from '@storybook/react-native-ui-common'; 4 | import { SafeAreaProvider } from 'react-native-safe-area-context'; 5 | import { theme, ThemeProvider } from '@storybook/react-native-theming'; 6 | 7 | export const decorators = [ 8 | (Story) => ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), 21 | ]; 22 | -------------------------------------------------------------------------------- /examples/expo-example/assets/storybook.icon/icon.json: -------------------------------------------------------------------------------- 1 | { 2 | "fill" : { 3 | "automatic-gradient" : "display-p3:0.97561,1.00000,1.00000,1.00000" 4 | }, 5 | "groups" : [ 6 | { 7 | "layers" : [ 8 | { 9 | "image-name" : "icon-storybook-default.svg", 10 | "name" : "icon-storybook-default", 11 | "position" : { 12 | "scale" : 14, 13 | "translation-in-points" : [ 14 | 0, 15 | 0 16 | ] 17 | } 18 | } 19 | ], 20 | "shadow" : { 21 | "kind" : "neutral", 22 | "opacity" : 0.5 23 | }, 24 | "translucency" : { 25 | "enabled" : true, 26 | "value" : 0.5 27 | } 28 | } 29 | ], 30 | "supported-platforms" : { 31 | "circles" : [ 32 | "watchOS" 33 | ], 34 | "squares" : "shared" 35 | } 36 | } -------------------------------------------------------------------------------- /examples/expo-example/.rnstorybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/react-native'; 2 | 3 | const main: StorybookConfig = { 4 | stories: [ 5 | '../components/**/*.stories.?(ts|tsx|js|jsx)', 6 | '../other_components/**/*.stories.?(ts|tsx|js|jsx)', 7 | { 8 | directory: '../../../packages/react-native-ui', 9 | titlePrefix: 'react-native-ui', 10 | files: '**/*.stories.?(ts|tsx|js|jsx)', 11 | }, 12 | ], 13 | addons: [ 14 | { name: '@storybook/addon-ondevice-controls' }, 15 | '@storybook/addon-ondevice-backgrounds', 16 | '@storybook/addon-ondevice-actions', 17 | '@storybook/addon-ondevice-notes', 18 | 'storybook-addon-deep-controls', 19 | './local-addon-example', 20 | ], 21 | reactNative: { 22 | playFn: false, 23 | }, 24 | 25 | framework: '@storybook/react-native', 26 | }; 27 | 28 | export default main; 29 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/Array/Array.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Array } from './Array'; 3 | 4 | const meta = { 5 | component: Array, 6 | args: { 7 | list: ['a', 'b', 'c'], 8 | }, 9 | argTypes: { 10 | list: { 11 | separator: ',', 12 | // @ts-expect-error 13 | control: { type: 'array' }, 14 | }, 15 | }, 16 | parameters: { 17 | notes: ` 18 | This is actually not a proper control type and should be inferred from the object type but is currently an inconsistency in the rn addon. 19 | `, 20 | storybookUIVisibility: 'hidden', // visible, hidden 21 | layout: 'centered', // fullscreen, centered, padded 22 | }, 23 | } satisfies Meta; 24 | 25 | export default meta; 26 | 27 | type Story = StoryObj; 28 | 29 | export const Basic: Story = {}; 30 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/stories/Header.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | 3 | import { Header } from './Header'; 4 | 5 | const meta = { 6 | title: 'Example/Header', 7 | component: Header, 8 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs 9 | tags: ['autodocs'], 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | 14 | type Story = StoryObj; 15 | 16 | export const LoggedIn: Story = { 17 | args: { 18 | user: { 19 | name: 'Jane Doe', 20 | }, 21 | onLogin: () => {}, 22 | onLogout: () => {}, 23 | onCreateAccount: () => {}, 24 | }, 25 | }; 26 | 27 | export const LoggedOut: Story = { 28 | args: { 29 | onLogin: () => {}, 30 | onLogout: () => {}, 31 | onCreateAccount: () => {}, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/CollapseAllIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | import { useTheme } from '@storybook/react-native-theming'; 3 | import { useMemo } from 'react'; 4 | 5 | export const CollapseAllIcon = ({ 6 | color, //= '#2E3438', 7 | width = 14, 8 | height = 14, 9 | ...props 10 | }: SvgProps) => { 11 | const theme = useTheme(); 12 | 13 | const fillColor = useMemo(() => { 14 | return color ?? theme.color.defaultText; 15 | }, [color, theme.color.defaultText]); 16 | 17 | return ( 18 | 19 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react-native/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig as StorybookConfigBase } from 'storybook/internal/types'; 2 | import type { ReactNativeOptions } from './Start'; 3 | export { darkTheme, theme, type Theme } from '@storybook/react-native-theming'; 4 | 5 | export { start, prepareStories, getProjectAnnotations, updateView } from './Start'; 6 | export type { View, Storage, InitialSelection, ThemePartial, Params } from './View'; 7 | 8 | export interface StorybookConfig { 9 | stories: StorybookConfigBase['stories']; 10 | addons: Array }>; 11 | // TODO move this to params 12 | reactNative?: ReactNativeOptions; 13 | framework?: '@storybook/react-native'; 14 | } 15 | 16 | export type { 17 | Meta, 18 | StoryFn, 19 | StoryObj, 20 | Args, 21 | ArgTypes, 22 | Preview, 23 | Decorator, 24 | Loader, 25 | Parameters, 26 | } from '@storybook/react'; 27 | -------------------------------------------------------------------------------- /packages/ondevice-notes/src/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import { Text } from 'react-native'; 3 | 4 | export class ErrorBoundary extends React.Component< 5 | { children: ReactNode | ReactNode[] }, 6 | { hasError: boolean } 7 | > { 8 | constructor(props) { 9 | super(props); 10 | this.state = { hasError: false }; 11 | } 12 | 13 | static getDerivedStateFromError(_error) { 14 | // Update state so the next render will show the fallback UI. 15 | return { hasError: true }; 16 | } 17 | 18 | componentDidCatch(error, errorInfo) { 19 | // You can also log the error to an error reporting service 20 | console.warn(error, errorInfo); 21 | } 22 | 23 | render() { 24 | if (this.state.hasError) { 25 | // You can render any custom fallback UI 26 | return Something went wrong.; 27 | } 28 | 29 | return this.props.children; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/expo-example/components/BackgroundExample/BackgroundCsf.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { Text, StyleSheet } from 'react-native'; 3 | 4 | const Background = () => ( 5 | Change background color via Addons -> Background 6 | ); 7 | 8 | const styles = StyleSheet.create({ 9 | text: { color: 'black' }, 10 | }); 11 | 12 | const meta = { 13 | component: Background, 14 | parameters: { 15 | backgrounds: { 16 | default: 'warm', 17 | values: [ 18 | { name: 'warm', value: 'hotpink' }, 19 | { name: 'cool', value: 'deepskyblue' }, 20 | { name: 'white', value: 'white' }, 21 | { name: 'black', value: 'black' }, 22 | ], 23 | }, 24 | }, 25 | } satisfies Meta; 26 | 27 | export default meta; 28 | 29 | type Story = StoryObj; 30 | 31 | export const Basic: Story = {}; 32 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/ExpandAllIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | import { useTheme } from '@storybook/react-native-theming'; 3 | import { useMemo } from 'react'; 4 | 5 | export const ExpandAllIcon = ({ 6 | color, //= '#2E3438', 7 | width = 14, 8 | height = 14, 9 | ...props 10 | }: SvgProps) => { 11 | const theme = useTheme(); 12 | 13 | const fillColor = useMemo(() => { 14 | return color ?? theme.color.defaultText; 15 | }, [color, theme.color.defaultText]); 16 | 17 | return ( 18 | 19 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /examples/expo-example/components/SafeAreaExample/UsableArea.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { View, StyleSheet, Text } from 'react-native'; 3 | 4 | function UsableAreaContent() { 5 | return ( 6 | 15 | This box should reach all corners of the content area. 16 | 17 | ); 18 | } 19 | const meta = { 20 | component: UsableAreaContent, 21 | } satisfies Meta; 22 | 23 | export default meta; 24 | 25 | type UsableAreaStory = StoryObj; 26 | 27 | export const SafeArea: UsableAreaStory = { parameters: { noSafeArea: false } }; 28 | 29 | export const NoSafeArea: UsableAreaStory = { parameters: { noSafeArea: true } }; 30 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/color-picker/index.ts: -------------------------------------------------------------------------------- 1 | // credit to https://github.com/instea/react-native-color-picker 2 | export { fromHsv, toHsv } from './utils'; 3 | export { HoloColorPicker as ColorPicker } from './HoloColorPicker'; 4 | export { TriangleColorPicker } from './TriangleColorPicker'; 5 | export type HsvColor = { h: number; s: number; v: number }; 6 | 7 | export interface IPicker { 8 | color?: string | HsvColor; 9 | defaultColor?: string | HsvColor; 10 | oldColor?: string; 11 | style?: object; 12 | onColorSelected?: (selectedColor: string) => void; 13 | onColorChange?: (selectedColor: HsvColor) => void; 14 | onOldColorSelected?: (oldColor: string) => void; 15 | hideSliders?: boolean; 16 | } 17 | 18 | export interface SliderProps { 19 | onValueChange?: (value: number) => void; 20 | value?: number; 21 | } 22 | export interface IHoloPicker extends IPicker { 23 | sliderComponent?: React.Component; 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/BottomBarToggleIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Path, Svg, SvgProps } from 'react-native-svg'; 2 | 3 | export const BottomBarToggleIcon = ({ 4 | color = 'currentColor', 5 | width = 14, 6 | height = 14, 7 | ...props 8 | }: SvgProps) => { 9 | return ( 10 | 11 | 15 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/ModalPortal.tsx: -------------------------------------------------------------------------------- 1 | import { Portal } from '@gorhom/portal'; 2 | import { Modal, ModalProps, Platform, View } from 'react-native'; 3 | 4 | const modalSupported = Platform.OS !== 'macos'; 5 | 6 | export const ModalPortal = ({ 7 | children, 8 | visible, 9 | usePortal, 10 | ...props 11 | }: ModalProps & { usePortal?: boolean }) => { 12 | if (modalSupported && !usePortal) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | 20 | if (!visible) return null; 21 | 22 | return ( 23 | 24 | 35 | {children} 36 | 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/CloseFullscreenIcon.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import Svg, { Path, SvgProps } from 'react-native-svg'; 3 | import { useTheme } from '@storybook/react-native-theming'; 4 | 5 | export function CloseFullscreenIcon({ color, width = 14, height = 14, ...props }: SvgProps) { 6 | const theme = useTheme(); 7 | 8 | const fillColor = useMemo(() => { 9 | return color ?? theme.color.defaultText; 10 | }, [color, theme.color.defaultText]); 11 | 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /docs/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; 2 | 3 | // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) 4 | 5 | /** 6 | * Creating a sidebar enables you to: 7 | - create an ordered group of docs 8 | - render a sidebar for each doc of that group 9 | - provide next/previous navigation 10 | 11 | The sidebars can be generated from the filesystem, or explicitly defined here. 12 | 13 | Create as many sidebars as you want. 14 | */ 15 | const sidebars: SidebarsConfig = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | 'intro', 23 | 'hello', 24 | { 25 | type: 'category', 26 | label: 'Tutorial', 27 | items: ['tutorial-basics/create-a-document'], 28 | }, 29 | ], 30 | */ 31 | }; 32 | 33 | export default sidebars; 34 | -------------------------------------------------------------------------------- /packages/ondevice-notes/src/register.tsx: -------------------------------------------------------------------------------- 1 | import { type API, addons, types } from 'storybook/manager-api'; 2 | import { Notes } from './components/Notes'; 3 | 4 | import type { Args, StoryContext } from 'storybook/internal/csf'; 5 | import type { ReactRenderer } from '@storybook/react'; 6 | 7 | import type { Channel } from 'storybook/internal/channels'; 8 | export const PARAM_KEY = 'notes'; 9 | 10 | export interface Selection { 11 | storyId: string; 12 | viewMode: 'story'; 13 | } 14 | 15 | export type StoryFromId = StoryContext; 16 | 17 | type ApiStore = { 18 | fromId: (id: any) => StoryFromId; 19 | getSelection: () => Selection; 20 | _channel: Channel; 21 | }; 22 | 23 | export type RNAddonApi = API & { store: () => ApiStore }; 24 | 25 | addons.register('storybook/notes', (api: RNAddonApi) => { 26 | addons.add('storybook/notes/panel', { 27 | type: types.PANEL, 28 | title: 'Notes', 29 | render: ({ active }) => , 30 | paramKey: PARAM_KEY, 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/types/Text.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | import { Input } from './common'; 4 | import { useResyncValue } from './useResyncValue'; 5 | 6 | export interface TextProps { 7 | arg: { 8 | name: string; 9 | value: string; 10 | type: string; 11 | }; 12 | onChange: (value: any) => void; 13 | isPristine: boolean; 14 | } 15 | 16 | const TextType = ({ arg, onChange, isPristine }: TextProps) => { 17 | const { setCurrentValue, key } = useResyncValue(arg.value, isPristine); 18 | 19 | const [focused, setFocused] = useState(false); 20 | 21 | return ( 22 | { 27 | onChange(text); 28 | setCurrentValue(text); 29 | }} 30 | autoCapitalize="none" 31 | underlineColorAndroid="transparent" 32 | onFocus={() => setFocused(true)} 33 | onBlur={() => setFocused(false)} 34 | focused={focused} 35 | /> 36 | ); 37 | }; 38 | 39 | export default TextType; 40 | -------------------------------------------------------------------------------- /examples/expo-example/components/InputExample/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, TextInput } from 'react-native'; 2 | 3 | const styles = StyleSheet.create({ 4 | input: { borderColor: 'darkgrey', borderWidth: 1, padding: 8, borderRadius: 4 }, 5 | }); 6 | 7 | export type InputProps = { 8 | placeholder?: string; 9 | borderColor?: string; 10 | onChangeText?: (text: string) => void; 11 | defaultValue?: string; 12 | keyboardType?: 13 | | 'default' 14 | | 'number-pad' 15 | | 'decimal-pad' 16 | | 'numeric' 17 | | 'email-address' 18 | | 'phone-pad' 19 | | 'url'; 20 | }; 21 | 22 | export const Input = ({ 23 | placeholder, 24 | borderColor = 'darkgrey', 25 | onChangeText, 26 | defaultValue, 27 | keyboardType, 28 | }: InputProps) => { 29 | return ( 30 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/LayoutProvider.tsx: -------------------------------------------------------------------------------- 1 | import type { FC, PropsWithChildren } from 'react'; 2 | import { createContext, useContext, useMemo } from 'react'; 3 | import { Platform, useWindowDimensions } from 'react-native'; 4 | import { BREAKPOINT } from './constants'; 5 | 6 | type LayoutContextType = { 7 | isDesktop: boolean; 8 | isMobile: boolean; 9 | }; 10 | 11 | const LayoutContext = createContext({ 12 | isDesktop: false, 13 | isMobile: true, 14 | }); 15 | 16 | export const LayoutProvider: FC = ({ children }) => { 17 | const { width } = useWindowDimensions(); 18 | const isDesktop = Platform.OS === 'macos' || Platform.OS === 'windows' || width >= BREAKPOINT; 19 | const isMobile = !isDesktop; 20 | 21 | const contextValue = useMemo( 22 | () => ({ 23 | isDesktop, 24 | isMobile, 25 | }), 26 | [isDesktop, isMobile] 27 | ); 28 | 29 | return {children}; 30 | }; 31 | 32 | export const useLayout = () => useContext(LayoutContext); 33 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/README.md: -------------------------------------------------------------------------------- 1 | # Storybook Backgrounds Addon for React Native 2 | 3 | Storybook Backgrounds Addon for React Native can be used to change background colors of your stories right from the device. 4 | 5 | ## Installation 6 | 7 | ```sh 8 | yarn add -D @storybook/addon-ondevice-backgrounds 9 | ``` 10 | 11 | ## Configuration 12 | 13 | Then, add following content to `.rnstorybook/main.ts`: 14 | 15 | ```ts 16 | import type { StorybookConfig } from '@storybook/react-native'; 17 | 18 | const main: StorybookConfig = { 19 | addons: ['@storybook/addon-ondevice-backgrounds'], 20 | }; 21 | 22 | export default main; 23 | ``` 24 | 25 | ## Usage 26 | 27 | See the [example of using the Backgrounds Addon with Component Story Format](../../examples/expo-example/components/BackgroundExample/BackgroundCsf.stories.tsx). You can also run the [react-native app](../../examples/expo-example) to see it in action. 28 | 29 | The [web Backgrounds Addon documentation](https://storybook.js.org/docs/react/essentials/backgrounds) may also be useful, but the examples there have not been tested with Storybook for React Native. 30 | -------------------------------------------------------------------------------- /examples/expo-example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Expo Example", 3 | "slug": "expo-example", 4 | "version": "1.0.0", 5 | "orientation": "portrait", 6 | "scheme": "storybook", 7 | "userInterfaceStyle": "automatic", 8 | "newArchEnabled": true, 9 | "icon": "./assets/icon.png", 10 | "ios": { 11 | "bundleIdentifier": "com.storybook.example", 12 | "icon": "./assets/storybook.icon", 13 | "infoPlist": { 14 | "ITSAppUsesNonExemptEncryption": false 15 | } 16 | }, 17 | "android": { 18 | "package": "com.storybook.example", 19 | "edgeToEdgeEnabled": true 20 | }, 21 | "web": { 22 | "bundler": "metro", 23 | "output": "single", 24 | "favicon": "./assets/icon.png" 25 | }, 26 | "experiments": { 27 | "reactCompiler": true, 28 | "tsconfigPaths": true 29 | }, 30 | "owner": "react-native-storybook", 31 | "runtimeVersion": { 32 | "policy": "appVersion" 33 | }, 34 | "extra": { 35 | "eas": { 36 | "projectId": "ebfab020-5dff-44a5-a29e-a0a6e6c175b9" 37 | } 38 | }, 39 | "updates": { 40 | "url": "https://u.expo.dev/ebfab020-5dff-44a5-a29e-a0a6e6c175b9" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/expo-example/components/StoryName/StoryName.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text } from 'react-native'; 3 | 4 | const StoryName = ({ name }: { name: string }) => {name}; 5 | 6 | const meta = { 7 | component: StoryName, 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | 12 | type Story = StoryObj; 13 | 14 | export const WithStoryName: Story = { 15 | storyName: 'story name here', 16 | args: { 17 | name: 'story name here', 18 | }, 19 | }; 20 | 21 | export const WithName: Story = { 22 | storyName: 'name here', 23 | args: { 24 | name: 'name here', 25 | }, 26 | }; 27 | 28 | export const WithNoName: Story = { 29 | args: { 30 | name: 'no name here', 31 | }, 32 | }; 33 | 34 | export const CSF2Example = (args: { name: string }) => ; 35 | CSF2Example.args = { name: 'csf2 with no name' }; 36 | 37 | export const CSF2ExampleWithName = (args: { name: string }) => ; 38 | CSF2ExampleWithName.storyName = 'csf2 with name'; 39 | CSF2ExampleWithName.args = { name: 'csf2 with name' }; 40 | -------------------------------------------------------------------------------- /packages/ondevice-actions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook/addon-ondevice-actions", 3 | "version": "10.1.1", 4 | "description": "Action Logger addon for react-native storybook", 5 | "keywords": [ 6 | "storybook" 7 | ], 8 | "homepage": "https://storybook.js.org/", 9 | "bugs": { 10 | "url": "https://github.com/storybookjs/react-native/issues" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/storybookjs/react-native.git" 15 | }, 16 | "license": "MIT", 17 | "main": "dist/index.js", 18 | "files": [ 19 | "dist/**/*", 20 | "docs/**/*", 21 | "README.md", 22 | "*.js", 23 | "*.d.ts" 24 | ], 25 | "scripts": { 26 | "dev": "tsc --watch", 27 | "prepare": "tsc" 28 | }, 29 | "dependencies": { 30 | "fast-deep-equal": "^3.1.3" 31 | }, 32 | "devDependencies": { 33 | "typescript": "~5.9.3" 34 | }, 35 | "peerDependencies": { 36 | "react": "*", 37 | "react-native": "*", 38 | "storybook": ">=10 || ^10" 39 | }, 40 | "publishConfig": { 41 | "access": "public" 42 | }, 43 | "gitHead": "4aa2ae40569ea7f61e438ce568a39c580b3097d8" 44 | } 45 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/src/container.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useState, useEffect } from 'react'; 2 | import { StyleSheet, View } from 'react-native'; 3 | import Constants from './constants'; 4 | import { Channel } from './BackgroundPanel'; 5 | 6 | interface ContainerProps { 7 | initialBackground: string; 8 | channel: Channel; 9 | children: ReactNode; 10 | } 11 | 12 | const Container = ({ initialBackground, channel, children }: ContainerProps) => { 13 | const [background, setBackground] = useState(initialBackground || ''); 14 | 15 | useEffect(() => { 16 | channel.on(Constants.UPDATE_BACKGROUND, setBackground); 17 | return () => { 18 | channel.removeListener(Constants.UPDATE_BACKGROUND, setBackground); 19 | }; 20 | }, [channel]); 21 | 22 | return ( 23 | 27 | {children} 28 | 29 | ); 30 | }; 31 | 32 | export default Container; 33 | 34 | const styles = StyleSheet.create({ 35 | container: { flex: 1, backgroundColor: 'transparent' }, 36 | }); 37 | -------------------------------------------------------------------------------- /examples/expo-example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | 34 | # node.js 35 | # 36 | node_modules/ 37 | npm-debug.log 38 | yarn-error.log 39 | 40 | # BUCK 41 | buck-out/ 42 | \.buckd/ 43 | *.keystore 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://docs.fastlane.tools/best-practices/source-control/ 51 | 52 | */fastlane/report.xml 53 | */fastlane/Preview.html 54 | */fastlane/screenshots 55 | 56 | # Bundle artifacts 57 | *.jsbundle 58 | 59 | # CocoaPods 60 | /ios/Pods/ 61 | 62 | # Expo 63 | .expo/* 64 | web-build/ 65 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/FullscreenIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Svg, { Path, SvgProps } from 'react-native-svg'; 3 | import { useTheme } from '@storybook/react-native-theming'; 4 | import { useMemo } from 'react'; 5 | 6 | export function FullscreenIcon({ color, width = 14, height = 14, ...props }: SvgProps) { 7 | const theme = useTheme(); 8 | 9 | const fillColor = useMemo(() => { 10 | return color ?? theme.color.defaultText; 11 | }, [color, theme.color.defaultText]); 12 | 13 | return ( 14 | 15 | 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kadira Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #029cfd; 10 | --ifm-color-primary-dark: #0288e4; 11 | --ifm-color-primary-darker: #0280d7; 12 | --ifm-color-primary-darkest: #026ab1; 13 | --ifm-color-primary-light: #1ba6fd; 14 | --ifm-color-primary-lighter: #28acfd; 15 | --ifm-color-primary-lightest: #4fb8fd; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #029cfd; 23 | --ifm-color-primary-dark: #0288e4; 24 | --ifm-color-primary-darker: #0280d7; 25 | --ifm-color-primary-darkest: #026ab1; 26 | --ifm-color-primary-light: #1ba6fd; 27 | --ifm-color-primary-lighter: #28acfd; 28 | --ifm-color-primary-lightest: #4fb8fd; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/PropField.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentType } from 'react'; 2 | import { styled } from '@storybook/react-native-theming'; 3 | import TypeMap from './types'; 4 | import { ArgType } from './ControlsPanel'; 5 | import { ControlTypes } from './sharedTypes'; 6 | 7 | export interface Knob { 8 | name: string; 9 | label: string; 10 | value: any; 11 | hideLabel: boolean; 12 | type: ControlTypes; 13 | groupId?: string; 14 | } 15 | 16 | const InvalidTypeText = styled.Text(({ theme }) => ({ 17 | color: theme.color.negative, 18 | })); 19 | 20 | const InvalidType = ({ arg }: { arg: ArgType }) => ( 21 | Invalid type: {arg.type} 22 | ); 23 | 24 | interface PropFieldProps { 25 | onChange: (value: any) => void; 26 | arg: ArgType; 27 | isPristine: boolean; 28 | } 29 | 30 | const PropField = ({ onChange, arg, isPristine }: PropFieldProps) => { 31 | const InputType: ComponentType = TypeMap[arg.type]; 32 | return InputType ? ( 33 | 34 | ) : ( 35 | 36 | ); 37 | }; 38 | 39 | export default PropField; 40 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/Explorer.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | import React, { useRef } from 'react'; 3 | import { Ref } from './Refs'; 4 | import type { CombinedDataset, Selection } from '@storybook/react-native-ui-common'; 5 | import { View } from 'react-native'; 6 | 7 | export interface ExplorerProps { 8 | isLoading: boolean; 9 | isBrowsing: boolean; 10 | dataset: CombinedDataset; 11 | selected: Selection; 12 | setSelection: (selection: Selection) => void; 13 | } 14 | 15 | export const Explorer: FC = React.memo(function Explorer({ 16 | isLoading, 17 | isBrowsing, 18 | dataset, 19 | selected, 20 | setSelection, 21 | }) { 22 | const containerRef = useRef(null); 23 | 24 | return ( 25 | 26 | {dataset.entries.map(([refId, ref]) => ( 27 | 35 | ))} 36 | 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/Explorer.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | import React, { useRef } from 'react'; 3 | import { Ref } from './Refs'; 4 | import type { CombinedDataset, Selection } from '@storybook/react-native-ui-common'; 5 | import { View } from 'react-native'; 6 | 7 | export interface ExplorerProps { 8 | isLoading: boolean; 9 | isBrowsing: boolean; 10 | dataset: CombinedDataset; 11 | selected: Selection; 12 | setSelection: (selection: Selection) => void; 13 | } 14 | 15 | export const Explorer: FC = React.memo(function Explorer({ 16 | isLoading, 17 | isBrowsing, 18 | dataset, 19 | selected, 20 | setSelection, 21 | }) { 22 | const containerRef = useRef(null); 23 | 24 | return ( 25 | 26 | {dataset.entries.map(([refId, ref]) => ( 27 | 35 | ))} 36 | 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /examples/expo-example/metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.io/guides/customizing-metro 2 | const { getDefaultConfig } = require('expo/metro-config'); 3 | const path = require('path'); 4 | 5 | const projectRoot = __dirname; 6 | const workspaceRoot = path.resolve(projectRoot, '../../'); 7 | 8 | /** 9 | * Metro configuration 10 | * https://reactnative.dev/docs/metro 11 | * 12 | * @type {import('metro-config').MetroConfig} 13 | */ 14 | const defaultConfig = getDefaultConfig(projectRoot); 15 | 16 | defaultConfig.watchFolders = [workspaceRoot]; 17 | 18 | defaultConfig.resolver.nodeModulesPaths = [ 19 | path.resolve(projectRoot, 'node_modules'), 20 | path.resolve(workspaceRoot, 'node_modules'), 21 | ]; 22 | 23 | const { withStorybook } = require('@storybook/react-native/metro/withStorybook'); 24 | 25 | module.exports = withStorybook(defaultConfig, { 26 | enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true', 27 | }); 28 | 29 | /* , { 30 | enabled: process.env.STORYBOOK_ENABLED === 'true', 31 | configPath: path.resolve(__dirname, './.rnstorybook'), 32 | // websockets: { 33 | // port: 7007, 34 | // host: '192.x.x.x', 35 | // host: 'localhost', 36 | // }, 37 | } */ 38 | -------------------------------------------------------------------------------- /packages/ondevice-notes/README.md: -------------------------------------------------------------------------------- 1 | # Storybook Notes Addon for react-native 2 | 3 | The Notes Addon allows you to write notes (text or markdown) for your stories in [Storybook](https://storybook.js.org). 4 | 5 | ## Installation 6 | 7 | ```sh 8 | yarn add -D @storybook/addon-ondevice-notes 9 | ``` 10 | 11 | ## Configuration 12 | 13 | Then, add following content to `.rnstorybook/main.ts`: 14 | 15 | ```ts 16 | import type { StorybookConfig } from '@storybook/react-native'; 17 | 18 | const main: StorybookConfig = { 19 | addons: ['@storybook/addon-ondevice-notes'], 20 | }; 21 | 22 | export default main; 23 | ``` 24 | 25 | ## Usage 26 | 27 | Use the `notes` parameter to add a note to stories: 28 | 29 | ```tsx 30 | import type { Meta } from '@storybook/react'; 31 | import { MyComponent } from './MyComponent'; 32 | 33 | const meta = { 34 | title: 'My title', 35 | component: MyComponent, 36 | parameters: { 37 | notes: ` 38 | # Here I can add some markdown 39 | 40 | Put a full new line between each element. 41 | `, 42 | }, 43 | } satisfies Meta; 44 | 45 | export default meta; 46 | ``` 47 | 48 | See the [example app](../../examples/expo-example) for more examples. 49 | -------------------------------------------------------------------------------- /packages/ondevice-actions/src/components/ActionLogger/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionDisplay } from 'storybook/actions'; 2 | import { Button, ScrollView, StyleSheet, Text, View } from 'react-native'; 3 | import Inspect from './Inspect'; 4 | 5 | interface ActionLoggerProps { 6 | actions: ActionDisplay[]; 7 | onClear: () => void; 8 | } 9 | 10 | export const ActionLogger = ({ actions, onClear }: ActionLoggerProps) => ( 11 | 12 | 13 | 14 | {actions.map((action: ActionDisplay) => ( 15 | 16 | {action.count > 1 ? {action.count} : null} 17 | 18 | 19 | 20 | 21 | ))} 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | 30 | export default ActionLogger; 31 | 32 | const styles = StyleSheet.create({ 33 | grow: { flexGrow: 1 }, 34 | row: { flexDirection: 'row' }, 35 | }); 36 | -------------------------------------------------------------------------------- /examples/expo-example/components/HiddenControls/HiddenControls.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react-native'; 2 | import { Text, View } from 'react-native'; 3 | 4 | type HiddenControlsProps = { 5 | value: number; 6 | padding: number; 7 | advanced: boolean; 8 | }; 9 | const HiddenControls = ({ value, padding }: HiddenControlsProps) => { 10 | return ( 11 | 12 | 13 | This is a story that allows you to hide controls based on other args. By default, you should 14 | be able to modify the value but not the padding of this component 15 | 16 | Your current chosen value is: {value} 17 | 18 | ); 19 | }; 20 | 21 | const meta = { 22 | component: HiddenControls, 23 | } satisfies Meta; 24 | 25 | export default meta; 26 | 27 | type HiddenControlsStory = StoryObj; 28 | 29 | export const Basic: HiddenControlsStory = { 30 | argTypes: { 31 | value: { control: 'number' }, 32 | advanced: { control: 'boolean' }, 33 | padding: { control: 'number', if: { arg: 'advanced' } }, 34 | }, 35 | args: { 36 | padding: 10, 37 | value: 42, 38 | advanced: false, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/components/SliderWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CustomSlider from './Slider'; 3 | import { Platform } from 'react-native'; 4 | import type { SliderProps } from '@react-native-community/slider'; 5 | 6 | let Slider: React.ComponentType = null; 7 | 8 | try { 9 | Slider = require('@react-native-community/slider').default; 10 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 11 | } catch (error) {} 12 | 13 | interface SliderWrapperProps { 14 | minimumValue?: number; 15 | maximumValue?: number; 16 | step?: number; 17 | value?: number; 18 | onValueChange?: (value: number) => void; 19 | onSlidingComplete?: (value: number) => void; 20 | disabled?: boolean; 21 | style?: any; 22 | useBuiltInSlider?: boolean; 23 | } 24 | 25 | const supportedPlatforms = ['android', 'ios', 'windows', 'web']; 26 | const nativeSliderSupported = supportedPlatforms.includes(Platform.OS); 27 | 28 | const SliderWrapper = ({ useBuiltInSlider = false, ...props }: SliderWrapperProps) => { 29 | if (!nativeSliderSupported || useBuiltInSlider || Slider === null) { 30 | return ; 31 | } 32 | return ; 33 | }; 34 | 35 | export default SliderWrapper; 36 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/icon/CollapseIcon.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@storybook/react-native-theming'; 2 | import type { FC } from 'react'; 3 | import Svg, { Path, SvgProps } from 'react-native-svg'; 4 | 5 | interface CollapseIconProps { 6 | isExpanded: boolean; 7 | } 8 | 9 | export const CollapseIconWrapper = styled.View<{ isExpanded: boolean }>( 10 | ({ theme, isExpanded }) => ({ 11 | width: 8, 12 | height: 8, 13 | display: 'flex', 14 | justifyContent: 'center', 15 | alignItems: 'center', 16 | color: theme.textMutedColor, 17 | transform: isExpanded ? 'rotateZ(90deg)' : 'none', 18 | transition: 'transform .1s ease-out', 19 | }) 20 | ); 21 | 22 | export const CollapseIcon: FC = ({ isExpanded }) => ( 23 | 24 | {/* */} 25 | 26 | 27 | ); 28 | 29 | export function CollapseSVG(props: SvgProps) { 30 | return ( 31 | 32 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /packages/react-native-ui-lite/src/Explorer.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Explorer } from './Explorer'; 2 | import { mockDataset } from './mockdata'; 3 | import type { RefType } from '@storybook/react-native-ui-common'; 4 | import { View } from 'react-native'; 5 | 6 | export default { 7 | component: Explorer, 8 | title: 'UI/Sidebar/Explorer', 9 | parameters: { layout: 'fullscreen', theme: 'side-by-side' }, 10 | decorators: [ 11 | (storyFn: any) => {storyFn()}, 12 | (storyFn: any) => {storyFn()}, 13 | ], 14 | }; 15 | 16 | const selected = { 17 | refId: 'storybook_internal', 18 | storyId: 'root-1-child-a2--grandchild-a1-1', 19 | }; 20 | 21 | const simple: Record = { 22 | storybook_internal: { 23 | title: undefined, 24 | id: 'storybook_internal', 25 | url: 'iframe.html', 26 | previewInitialized: true, 27 | // @ts-expect-error (invalid input) 28 | index: mockDataset.withRoot, 29 | }, 30 | }; 31 | 32 | export const Simple = () => ( 33 | {}} 39 | /> 40 | ); 41 | -------------------------------------------------------------------------------- /examples/expo-example/.rnstorybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import { Appearance } from 'react-native'; 2 | import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds'; 3 | import type { Preview } from '@storybook/react-native'; 4 | 5 | const preview: Preview = { 6 | decorators: [withBackgrounds], 7 | parameters: { 8 | actions: { argTypesRegex: '^on[A-Z].*' }, 9 | controls: { 10 | matchers: { 11 | color: /(background|color)$/i, 12 | date: /Date$/, 13 | }, 14 | }, 15 | options: { 16 | storySort: { 17 | method: 'alphabetical', 18 | includeNames: true, 19 | order: ['ControlExamples', ['ControlExample'], 'InteractionExample', 'DeepControls'], 20 | }, 21 | }, 22 | hideFullScreenButton: false, 23 | noSafeArea: false, 24 | my_param: 'anything', 25 | layout: 'padded', // fullscreen, centered, padded 26 | storybookUIVisibility: 'visible', // visible, hidden 27 | backgrounds: { 28 | default: Appearance.getColorScheme() === 'dark' ? 'dark' : 'plain', 29 | values: [ 30 | { name: 'plain', value: 'white' }, 31 | { name: 'dark', value: '#333' }, 32 | { name: 'app', value: '#eeeeee' }, 33 | ], 34 | }, 35 | }, 36 | }; 37 | 38 | export default preview; 39 | -------------------------------------------------------------------------------- /packages/react-native/src/components/StoryView/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import { View, Text } from 'react-native'; 3 | 4 | export class ErrorBoundary extends React.Component< 5 | { children: ReactNode | ReactNode[]; onError: (error: Error, stack: string) => void }, 6 | { hasError: boolean } 7 | > { 8 | constructor(props) { 9 | super(props); 10 | this.state = { hasError: false }; 11 | } 12 | 13 | static getDerivedStateFromError(_error) { 14 | // Update state so the next render will show the fallback UI. 15 | return { hasError: true }; 16 | } 17 | 18 | componentDidCatch(error, info) { 19 | this.props.onError(error, info.componentStack); 20 | } 21 | 22 | render() { 23 | if (this.state.hasError) { 24 | return ( 25 | 36 | Something went wrong rendering your story 37 | 38 | ); 39 | } 40 | 41 | return this.props.children; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/NoControlsWarning.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@storybook/react-native-theming'; 2 | import { Linking, View } from 'react-native'; 3 | 4 | const Paragraph = styled.Text(({ theme }) => ({ 5 | marginBottom: 16, 6 | color: theme.color.defaultText, 7 | })); 8 | 9 | const LinkText = styled.Text(({ theme }) => ({ 10 | color: theme.color.secondary, 11 | textDecorationLine: 'underline', 12 | })); 13 | 14 | const NoControlsWarning = () => { 15 | return ( 16 | 17 | This story is not configured to handle controls. 18 | 19 | Linking.openURL('https://storybook.js.org/docs/react/essentials/controls')} 21 | > 22 | Learn how to add controls 23 | {' '} 24 | and see{' '} 25 | 27 | Linking.openURL( 28 | 'https://github.com/storybookjs/react-native/tree/next-6.0/examples/expo-example/components/ControlExamples' 29 | ) 30 | } 31 | > 32 | examples in the Storybook React Native repository. 33 | 34 | 35 | 36 | ); 37 | }; 38 | 39 | export default NoControlsWarning; 40 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: [next] 6 | # Allows you to run this workflow manually from the Actions tab on GitHub. 7 | workflow_dispatch: 8 | 9 | # Allow this job to clone the repo and create a page deployment 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout your repository using git 20 | uses: actions/checkout@v4 21 | 22 | - name: Set node version 23 | uses: actions/setup-node@v4 24 | with: 25 | node-version: 24 26 | 27 | - name: Install 28 | shell: 'bash' 29 | run: yarn install 30 | 31 | - name: Build 32 | shell: 'bash' 33 | working-directory: docs 34 | run: yarn build 35 | 36 | - name: Upload Pages Artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | path: 'docs/build/' 40 | 41 | deploy: 42 | needs: build 43 | runs-on: ubuntu-latest 44 | environment: 45 | name: github-pages 46 | url: ${{ steps.deployment.outputs.page_url }} 47 | steps: 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /packages/ondevice-notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook/addon-ondevice-notes", 3 | "version": "10.1.1", 4 | "description": "Write notes for your react-native Storybook stories.", 5 | "keywords": [ 6 | "addon", 7 | "notes", 8 | "react-native", 9 | "storybook" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/storybookjs/react-native.git", 14 | "directory": "packages/ondevice-notes" 15 | }, 16 | "license": "MIT", 17 | "main": "dist/index.js", 18 | "types": "dist/index.d.ts", 19 | "files": [ 20 | "dist/**/*", 21 | "docs/**/*", 22 | "README.md", 23 | "*.js", 24 | "*.d.ts" 25 | ], 26 | "scripts": { 27 | "preprepare": "rm -rf dist/", 28 | "prepare": "tsup", 29 | "dev": "tsup --watch" 30 | }, 31 | "dependencies": { 32 | "@storybook/react-native-theming": "^10.1.1" 33 | }, 34 | "devDependencies": { 35 | "react-native-markdown-display": "^7.0.2", 36 | "tsup": "^8.5.0", 37 | "typescript": "~5.9.3" 38 | }, 39 | "peerDependencies": { 40 | "@storybook/react": "^10", 41 | "react": "*", 42 | "react-native": "*", 43 | "storybook": ">=10 || ^10" 44 | }, 45 | "publishConfig": { 46 | "access": "public" 47 | }, 48 | "gitHead": "4aa2ae40569ea7f61e438ce568a39c580b3097d8" 49 | } 50 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/util/useStyle.ts: -------------------------------------------------------------------------------- 1 | import { DependencyList, useMemo } from 'react'; 2 | import { ImageStyle, StyleProp, TextStyle, ViewStyle } from 'react-native'; 3 | 4 | /** 5 | * A hook to memoize a style. Uses `ViewStyle` per default, but can be used with other styles deriving from `FlexStyle` as well, such as `TextStyle`. 6 | * @param styleFactory The function that returns a style 7 | * @param deps The dependencies to trigger memoization re-evaluation 8 | * @see ["Memoize!!! 💾 - a react (native) performance guide"](https://gist.github.com/mrousavy/0de7486814c655de8a110df5cef74ddc) 9 | * @example 10 | * 11 | * // simple object styles 12 | * const style1 = useStyle(() => ({ height: someDynamicValue }), [someDynamicValue]) 13 | * 14 | * // array styles 15 | * const style2 = useStyle( 16 | * () => [styles.container, props.style, { height: someDynamicValue }], 17 | * [props.style, someDynamicValue] 18 | * ); 19 | */ 20 | export const useStyle = < 21 | TStyle extends ViewStyle | TextStyle | ImageStyle, 22 | TOutput extends StyleProp, 23 | >( 24 | styleFactory: () => TOutput, 25 | deps?: DependencyList 26 | ): TOutput => 27 | // eslint-disable-next-line react-compiler/react-compiler 28 | // eslint-disable-next-line react-hooks/exhaustive-deps 29 | useMemo(styleFactory, deps); 30 | -------------------------------------------------------------------------------- /packages/react-native-ui-common/src/hooks/useLastViewed.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useRef } from 'react'; 2 | 3 | import type { Selection, StoryRef } from '../types'; 4 | 5 | export const useLastViewed = (selection: Selection) => { 6 | const lastViewedRef = useRef([]); 7 | 8 | const updateLastViewed = useCallback( 9 | (story: StoryRef) => { 10 | const items = lastViewedRef.current; 11 | const index = items.findIndex( 12 | ({ storyId, refId }) => storyId === story.storyId && refId === story.refId 13 | ); 14 | if (index === 0) return; 15 | if (index === -1) { 16 | lastViewedRef.current = [story, ...items]; 17 | } else { 18 | lastViewedRef.current = [story, ...items.slice(0, index), ...items.slice(index + 1)]; 19 | } 20 | }, 21 | [lastViewedRef] 22 | ); 23 | 24 | useEffect(() => { 25 | if (selection) updateLastViewed(selection); 26 | // eslint-disable-next-line react-compiler/react-compiler 27 | // eslint-disable-next-line react-hooks/exhaustive-deps 28 | }, [selection]); 29 | 30 | return { 31 | getLastViewed: useCallback(() => lastViewedRef.current, [lastViewedRef]), 32 | clearLastViewed: useCallback(() => { 33 | lastViewedRef.current = lastViewedRef.current.slice(0, 1); 34 | }, [lastViewedRef]), 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/react-native-theming/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook/react-native-theming", 3 | "version": "10.1.1", 4 | "description": "A wrapper library around emotion 11 to provide theming support for react-native storybook", 5 | "keywords": [ 6 | "react", 7 | "react-native", 8 | "storybook", 9 | "theming" 10 | ], 11 | "homepage": "https://storybook.js.org/", 12 | "bugs": { 13 | "url": "https://github.com/storybookjs/react-native/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/storybookjs/react-native.git", 18 | "directory": "packages/react-native-theming" 19 | }, 20 | "license": "MIT", 21 | "main": "dist/index.js", 22 | "types": "dist/index.d.ts", 23 | "scripts": { 24 | "dev": "npx --yes tsx ./scripts/gendtsdev.ts && tsup --watch", 25 | "prepare": "tsup && npx --yes tsx ./scripts/patchdts.ts" 26 | }, 27 | "dependencies": { 28 | "polished": "^4.3.1" 29 | }, 30 | "devDependencies": { 31 | "@emotion/native": "^11.11.0", 32 | "@emotion/react": "^11.14.0", 33 | "tsup": "^8.5.0" 34 | }, 35 | "peerDependencies": { 36 | "react": "*", 37 | "react-native": ">=0.57.0" 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | }, 42 | "files": [ 43 | "dist/**/*", 44 | "README.md" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /packages/react-native-ui/src/Explorer.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Explorer } from './Explorer'; 2 | import { mockDataset } from './mockdata'; 3 | import type { RefType } from '@storybook/react-native-ui-common'; 4 | import { View } from 'react-native'; 5 | import { decorators } from './decorators'; 6 | 7 | export default { 8 | component: Explorer, 9 | title: 'UI/Sidebar/Explorer', 10 | parameters: { layout: 'fullscreen', theme: 'side-by-side' }, 11 | decorators: [ 12 | (storyFn: any) => {storyFn()}, 13 | (storyFn: any) => {storyFn()}, 14 | ...decorators, 15 | ], 16 | }; 17 | 18 | const selected = { 19 | refId: 'storybook_internal', 20 | storyId: 'root-1-child-a2--grandchild-a1-1', 21 | }; 22 | 23 | const simple: Record = { 24 | storybook_internal: { 25 | title: undefined, 26 | id: 'storybook_internal', 27 | url: 'iframe.html', 28 | previewInitialized: true, 29 | // @ts-expect-error (invalid input) 30 | index: mockDataset.withRoot, 31 | }, 32 | }; 33 | 34 | export const Simple = () => ( 35 | {}} 41 | /> 42 | ); 43 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook/addon-ondevice-backgrounds", 3 | "version": "10.1.1", 4 | "description": "A react-native storybook addon to show different backgrounds for your preview", 5 | "keywords": [ 6 | "addon", 7 | "background", 8 | "react", 9 | "storybook" 10 | ], 11 | "homepage": "https://storybook.js.org", 12 | "bugs": { 13 | "url": "https://github.com/storybookjs/react-native/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/storybookjs/react-native.git", 18 | "directory": "packages/ondevice-backgrounds" 19 | }, 20 | "license": "MIT", 21 | "main": "dist/index.js", 22 | "types": "dist/index.d.ts", 23 | "files": [ 24 | "dist/**/*", 25 | "docs/**/*", 26 | "README.md", 27 | "*.js", 28 | "*.d.ts" 29 | ], 30 | "scripts": { 31 | "prepare": "tsc", 32 | "dev": "tsc --watch" 33 | }, 34 | "dependencies": { 35 | "@storybook/react-native-theming": "^10.1.1" 36 | }, 37 | "devDependencies": { 38 | "typescript": "~5.9.3" 39 | }, 40 | "peerDependencies": { 41 | "react": "*", 42 | "react-native": "*", 43 | "storybook": ">=10 || ^10" 44 | }, 45 | "publishConfig": { 46 | "access": "public" 47 | }, 48 | "gitHead": "4aa2ae40569ea7f61e438ce568a39c580b3097d8" 49 | } 50 | -------------------------------------------------------------------------------- /examples/expo-example/components/LoginDocsExample/LoginForm/LoginForm.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { fn } from 'storybook/test'; 3 | import { LoginForm } from './LoginForm'; 4 | 5 | const meta = { 6 | component: LoginForm, 7 | args: { 8 | onSubmit: fn(), 9 | }, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | type Story = StoryObj; 14 | 15 | export const Default: Story = {}; 16 | 17 | export const WithErrors: Story = { 18 | args: { 19 | emailError: 'Please enter a valid email', 20 | passwordError: 'Password must be at least 8 characters', 21 | }, 22 | }; 23 | 24 | export const Loading: Story = { 25 | args: { 26 | loading: true, 27 | }, 28 | }; 29 | 30 | export const EmailErrorOnly: Story = { 31 | args: { 32 | emailError: 'This email is already registered', 33 | }, 34 | }; 35 | 36 | export const PasswordErrorOnly: Story = { 37 | args: { 38 | passwordError: 'Password is incorrect', 39 | }, 40 | }; 41 | 42 | export const LongErrors: Story = { 43 | args: { 44 | emailError: 45 | 'The email address you entered is not valid. Please check the format and try again.', 46 | passwordError: 47 | 'Your password must be at least 8 characters long and contain both letters and numbers for security.', 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('eslint/config'); 2 | const expoConfig = require('eslint-config-expo/flat'); 3 | const globals = require('globals'); 4 | const reactCompiler = require('eslint-plugin-react-compiler'); 5 | const pluginDocusaurus = require('@docusaurus/eslint-plugin'); 6 | 7 | module.exports = defineConfig([ 8 | { 9 | ignores: [ 10 | '.yarn/**', 11 | '**/dist/**', 12 | 'packages/react-native/template/**/*', 13 | 'packages/react-native/src/rn-host-detect.js', 14 | '**/storybook.requires.ts', 15 | 'examples/expo-example/.expo/**/*', 16 | 'docs/build/**/*', 17 | ], 18 | }, 19 | ...expoConfig.map((config) => ({ 20 | ...config, 21 | ignores: [...(config.ignores || []), 'docs/**/*'], 22 | })), 23 | reactCompiler.configs.recommended, 24 | { 25 | files: ['**/*.spec.js', '**/*.spec.jsx', '**/*.test.js', '**/*.test.jsx'], 26 | languageOptions: { 27 | globals: { ...globals.jest, ...globals.node }, 28 | }, 29 | }, 30 | { 31 | rules: { 32 | '@typescript-eslint/no-require-imports': 'off', 33 | '@typescript-eslint/array-type': 'off', 34 | }, 35 | }, 36 | { 37 | files: ['docs/**/*.ts', 'docs/**/*.tsx'], 38 | plugins: { 39 | '@docusaurus': pluginDocusaurus, 40 | }, 41 | rules: pluginDocusaurus.configs.recommended.rules, 42 | }, 43 | ]); 44 | -------------------------------------------------------------------------------- /examples/expo-example/components/LoginDocsExample/Button/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | import { fn } from 'storybook/test'; 3 | import { Button } from './Button'; 4 | 5 | const meta = { 6 | component: Button, 7 | args: { 8 | onPress: fn(), 9 | }, 10 | } satisfies Meta; 11 | 12 | export default meta; 13 | type Story = StoryObj; 14 | 15 | export const Primary: Story = { 16 | args: { 17 | title: 'Sign In', 18 | }, 19 | }; 20 | 21 | export const Secondary: Story = { 22 | args: { 23 | title: 'Create Account', 24 | variant: 'secondary', 25 | }, 26 | }; 27 | 28 | export const Loading: Story = { 29 | args: { 30 | title: 'Sign In', 31 | loading: true, 32 | }, 33 | }; 34 | 35 | export const Disabled: Story = { 36 | args: { 37 | title: 'Sign In', 38 | disabled: true, 39 | }, 40 | }; 41 | 42 | export const SecondaryLoading: Story = { 43 | args: { 44 | title: 'Create Account', 45 | variant: 'secondary', 46 | loading: true, 47 | }, 48 | }; 49 | 50 | export const SecondaryDisabled: Story = { 51 | args: { 52 | title: 'Create Account', 53 | variant: 'secondary', 54 | disabled: true, 55 | }, 56 | }; 57 | 58 | export const LongTitle: Story = { 59 | args: { 60 | title: 'This is a very long button title that might affect layout', 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/stories/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react-native'; 2 | 3 | import { View } from 'react-native'; 4 | import { fn } from 'storybook/test'; 5 | 6 | import { Button } from './Button'; 7 | 8 | const meta = { 9 | title: 'Example/Button', 10 | component: Button, 11 | decorators: [ 12 | (Story) => ( 13 | 14 | 15 | 16 | ), 17 | ], 18 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs 19 | tags: ['autodocs'], 20 | // Use `fn` to spy on the onPress arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args 21 | args: { onPress: fn() }, 22 | } satisfies Meta; 23 | 24 | export default meta; 25 | 26 | type Story = StoryObj; 27 | 28 | export const Primary: Story = { 29 | args: { 30 | primary: true, 31 | label: 'Button', 32 | }, 33 | }; 34 | 35 | export const Secondary: Story = { 36 | args: { 37 | label: 'Button', 38 | }, 39 | }; 40 | 41 | export const Large: Story = { 42 | args: { 43 | size: 'large', 44 | label: 'Button', 45 | }, 46 | }; 47 | 48 | export const Small: Story = { 49 | args: { 50 | size: 'small', 51 | label: 'Button', 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "10.1.1", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc", 16 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx" 17 | }, 18 | "dependencies": { 19 | "@docusaurus/core": "3.9.2", 20 | "@docusaurus/faster": "3.9.2", 21 | "@docusaurus/preset-classic": "3.9.2", 22 | "@mdx-js/react": "^3.1.1", 23 | "clsx": "^2.1.1", 24 | "prism-react-renderer": "^2.4.1", 25 | "react": "19.1.0", 26 | "react-dom": "19.1.0" 27 | }, 28 | "devDependencies": { 29 | "@docusaurus/module-type-aliases": "3.9.2", 30 | "@docusaurus/tsconfig": "3.9.2", 31 | "@docusaurus/types": "3.9.2", 32 | "typescript": "~5.9.3" 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.5%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 3 chrome version", 42 | "last 3 firefox version", 43 | "last 5 safari version" 44 | ] 45 | }, 46 | "engines": { 47 | "node": ">=22.18.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | import Heading from '@theme/Heading'; 8 | 9 | import styles from './index.module.css'; 10 | 11 | function HomepageHeader() { 12 | const { siteConfig } = useDocusaurusContext(); 13 | return ( 14 | 15 | 16 | 17 | {siteConfig.title} 18 | 19 | {siteConfig.tagline} 20 | 21 | 22 | Get started 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default function Home(): ReactNode { 31 | const { siteConfig } = useDocusaurusContext(); 32 | return ( 33 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /examples/expo-example/components/LoginDocsExample/TextInput/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TextInput as RNTextInput, View, Text, StyleSheet } from 'react-native'; 3 | 4 | export interface TextInputProps { 5 | label?: string; 6 | placeholder?: string; 7 | value?: string; 8 | onChangeText?: (text: string) => void; 9 | secureTextEntry?: boolean; 10 | error?: string; 11 | } 12 | 13 | export const TextInput: React.FC = ({ 14 | label, 15 | placeholder, 16 | value, 17 | onChangeText, 18 | secureTextEntry, 19 | error, 20 | }) => { 21 | return ( 22 | 23 | {label && {label}} 24 | 31 | {error && {error}} 32 | 33 | ); 34 | }; 35 | 36 | const styles = StyleSheet.create({ 37 | container: { marginBottom: 16 }, 38 | label: { fontSize: 16, marginBottom: 8, fontWeight: '500' }, 39 | input: { 40 | borderWidth: 1, 41 | borderColor: '#ddd', 42 | padding: 12, 43 | borderRadius: 8, 44 | fontSize: 16, 45 | }, 46 | inputError: { borderColor: '#ff6b6b' }, 47 | error: { color: '#ff6b6b', fontSize: 14, marginTop: 4 }, 48 | }); 49 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { makeDecorator } from 'storybook/internal/preview-api'; 3 | import { addons } from 'storybook/manager-api'; 4 | 5 | import Events from './constants'; 6 | import Container from './container'; 7 | 8 | export interface Background { 9 | name: string; 10 | value: string; 11 | } 12 | 13 | export const withBackgrounds = makeDecorator({ 14 | name: 'withBackgrounds', 15 | parameterName: 'backgrounds', 16 | skipIfNoParametersOrOptions: true, 17 | 18 | wrapper: (getStory, context, { options, parameters }) => { 19 | const data = (parameters || options || { values: [] }) as { 20 | default?: string; 21 | values: Background[]; 22 | }; 23 | const backgrounds: Background[] = data.values; 24 | 25 | let background = 'transparent'; 26 | if (backgrounds.length !== 0) { 27 | addons.getChannel().emit(Events.SET, backgrounds); 28 | const defaultValue = data.default 29 | ? backgrounds.find((b) => b.name === data.default) 30 | : undefined; 31 | const defaultOrFirst = defaultValue ? defaultValue : backgrounds[0]; 32 | 33 | if (defaultOrFirst) { 34 | background = defaultOrFirst.value; 35 | } 36 | } 37 | 38 | return ( 39 | 40 | {getStory(context) as React.ReactNode} 41 | 42 | ); 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /examples/expo-example/.rnstorybook/index.tsx: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage'; 2 | import { LiteUI } from '@storybook/react-native-ui-lite'; 3 | import { StatusBar, View } from 'react-native'; 4 | import { SafeAreaView, SafeAreaProvider } from 'react-native-safe-area-context'; 5 | import { view } from './storybook.requires'; 6 | 7 | const isScreenshotTesting = process.env.EXPO_PUBLIC_SCREENSHOT_TESTING === 'true'; 8 | const isLiteUI = process.env.EXPO_PUBLIC_LITE_UI === 'true'; 9 | 10 | const StorybookUIRoot = view.getStorybookUI({ 11 | shouldPersistSelection: true, 12 | storage: { 13 | getItem: AsyncStorage.getItem, 14 | setItem: AsyncStorage.setItem, 15 | }, 16 | enableWebsockets: false, 17 | host: 'localhost', 18 | port: 7007, 19 | 20 | CustomUIComponent: isScreenshotTesting 21 | ? ({ children, story }) => { 22 | return ( 23 | 24 | 25 | 26 | 32 | {children} 33 | 34 | 35 | 36 | ); 37 | } 38 | : isLiteUI 39 | ? LiteUI 40 | : undefined, 41 | }); 42 | 43 | export default StorybookUIRoot; 44 | -------------------------------------------------------------------------------- /packages/react-native/template/cli/storybook.requires.ts: -------------------------------------------------------------------------------- 1 | /* do not change this file, it is auto generated by storybook. */ 2 | 3 | import { start, updateView } from '@storybook/react-native'; 4 | 5 | import '@storybook/addon-ondevice-controls/register'; 6 | import '@storybook/addon-ondevice-actions/register'; 7 | 8 | const normalizedStories = [ 9 | { 10 | titlePrefix: '', 11 | directory: './.rnstorybook/stories', 12 | files: '**/*.stories.?(ts|tsx|js|jsx)', 13 | importPathMatcher: 14 | /^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/, 15 | // @ts-ignore 16 | req: require.context( 17 | './stories', 18 | true, 19 | /^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/ 20 | ), 21 | }, 22 | ]; 23 | 24 | declare global { 25 | var view: ReturnType; 26 | var STORIES: typeof normalizedStories; 27 | } 28 | 29 | const annotations = [ 30 | require('./preview'), 31 | require('@storybook/react-native/dist/preview'), 32 | require('@storybook/addon-ondevice-actions/preview'), 33 | ]; 34 | 35 | global.STORIES = normalizedStories; 36 | 37 | // @ts-ignore 38 | module?.hot?.accept?.(); 39 | 40 | if (!global.view) { 41 | global.view = start({ 42 | annotations, 43 | storyEntries: normalizedStories, 44 | }); 45 | } else { 46 | updateView(global.view, annotations, normalizedStories); 47 | } 48 | 49 | export const view = global.view; 50 | -------------------------------------------------------------------------------- /examples/expo-example/components/ControlExamples/ControlExample/ControlExample.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from 'react-native'; 2 | 3 | export interface ControlExampleProps { 4 | backgroundColor: string; 5 | name: string; 6 | age: number; 7 | fruit: string; 8 | otherFruit: string; 9 | birthday: Date; 10 | dollars: number; 11 | items: string[]; 12 | nice: boolean; 13 | customStyles: Record; 14 | } 15 | 16 | export function ControlExample({ 17 | backgroundColor, 18 | name, 19 | age, 20 | fruit, 21 | otherFruit, 22 | birthday, 23 | dollars, 24 | items, 25 | nice, 26 | customStyles, 27 | }: ControlExampleProps) { 28 | const intro = `My name is ${name}, I'm ${age} years old, and my favorite fruit is ${fruit}. I also enjoy ${otherFruit}.`; 29 | const style = { backgroundColor, ...customStyles }; 30 | const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!'; 31 | const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' } as const; 32 | return ( 33 | 34 | {intro} 35 | My birthday is: {new Date(birthday).toLocaleDateString('en-US', dateOptions)} 36 | My wallet contains: ${dollars.toFixed(2)} 37 | In my backpack, I have: 38 | 39 | {items.map((item) => ( 40 | {item} 41 | ))} 42 | 43 | {salutation} 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /packages/ondevice-controls/README.md: -------------------------------------------------------------------------------- 1 | # Storybook Controls Addon for React Native 2 | 3 | Storybook Controls Addon for React Native allows editing a component's arguments dynamically via a graphical UI without needing to code. The Controls Addon replaces the old Knobs Addon. 4 | 5 | ## Installation 6 | 7 | Controls has some extra dependencies needed to display the form inputs. 8 | 9 | ```sh 10 | yarn add -D @storybook/addon-ondevice-controls @react-native-community/datetimepicker @react-native-community/slider 11 | ``` 12 | 13 | ## Configuration 14 | 15 | Then, add following content to `.rnstorybook/main.ts`: 16 | 17 | ```ts 18 | import type { StorybookConfig } from '@storybook/react-native'; 19 | 20 | const main: StorybookConfig = { 21 | addons: ['@storybook/addon-ondevice-controls'], 22 | }; 23 | 24 | export default main; 25 | ``` 26 | 27 | See the [examples of using the Controls Addon with Component Story Format](../../examples/expo-example/components/ControlExamples). You can also run the [react-native app](../../examples/expo-example) to see it in action. 28 | 29 | The [web Controls Addon documentation](https://storybook.js.org/docs/react/essentials/controls) may also be useful, but the examples there have not been tested with Storybook for React Native. 30 | 31 | ## Migrating from Knobs 32 | 33 | See [examples for migrating from Knobs to Controls](https://github.com/storybookjs/storybook/blob/next/code/addons/controls/README.md#how-do-i-migrate-from-addon-knobs) in the web Controls Addon README. 34 | -------------------------------------------------------------------------------- /examples/expo-example/components/LoginDocsExample/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native'; 3 | 4 | export interface ButtonProps { 5 | title: string; 6 | onPress?: () => void; 7 | variant?: 'primary' | 'secondary'; 8 | loading?: boolean; 9 | disabled?: boolean; 10 | } 11 | 12 | export const Button: React.FC = ({ 13 | title, 14 | onPress, 15 | variant = 'primary', 16 | loading = false, 17 | disabled = false, 18 | }) => { 19 | return ( 20 | 25 | {loading ? ( 26 | 27 | ) : ( 28 | {title} 29 | )} 30 | 31 | ); 32 | }; 33 | 34 | const styles = StyleSheet.create({ 35 | button: { 36 | padding: 16, 37 | borderRadius: 8, 38 | alignItems: 'center', 39 | justifyContent: 'center', 40 | minHeight: 48, 41 | }, 42 | primary: { backgroundColor: '#007AFF' }, 43 | secondary: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#007AFF' }, 44 | disabled: { opacity: 0.5 }, 45 | text: { fontSize: 16, fontWeight: '600' }, 46 | primaryText: { color: 'white' }, 47 | secondaryText: { color: '#007AFF' }, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/expo-example/components/LoginDocsExample/LoginForm/LoginForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { View, StyleSheet } from 'react-native'; 3 | import { TextInput } from '../TextInput/TextInput'; 4 | import { Button } from '../Button/Button'; 5 | 6 | export interface LoginFormProps { 7 | onSubmit?: (email: string, password: string) => void; 8 | loading?: boolean; 9 | emailError?: string; 10 | passwordError?: string; 11 | } 12 | 13 | export const LoginForm: React.FC = ({ 14 | onSubmit, 15 | loading = false, 16 | emailError, 17 | passwordError, 18 | }) => { 19 | const [email, setEmail] = useState(''); 20 | const [password, setPassword] = useState(''); 21 | 22 | const handleSubmit = () => { 23 | onSubmit?.(email, password); 24 | }; 25 | 26 | return ( 27 | 28 | 35 | 43 | 49 | 50 | ); 51 | }; 52 | 53 | const styles = StyleSheet.create({ 54 | container: { padding: 16, gap: 16 }, 55 | }); 56 | -------------------------------------------------------------------------------- /examples/expo-example/components/SafeAreaExample/SafeAreaInside.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryObj, Meta } from '@storybook/react-native'; 2 | import { AButton } from './AButton'; 3 | import { ScrollView } from 'react-native'; 4 | 5 | const InsideSafeAreaMeta: Meta = { 6 | component: AButton, 7 | argTypes: { 8 | onPress: { action: 'pressed the button' }, 9 | }, 10 | args: { 11 | text: 'Regular layout', 12 | }, 13 | }; 14 | export default InsideSafeAreaMeta; 15 | 16 | type InsideSafeAreaStory = StoryObj; 17 | 18 | export const Basic: InsideSafeAreaStory = {}; 19 | 20 | export const ListBasic: InsideSafeAreaStory = { 21 | render: (args) => ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ), 54 | }; 55 | -------------------------------------------------------------------------------- /packages/ondevice-backgrounds/src/Swatch.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@storybook/react-native-theming'; 2 | 3 | interface SwatchProps { 4 | name: string; 5 | value: string; 6 | setBackground: (background: string) => void; 7 | } 8 | 9 | const PressableSwatch = styled.TouchableOpacity(({ theme }) => ({ 10 | marginBottom: 10, 11 | borderWidth: 1, 12 | borderColor: theme.appBorderColor, 13 | borderRadius: 6, 14 | backgroundColor: theme.background.content, 15 | paddingVertical: 4, 16 | paddingHorizontal: 4, 17 | })); 18 | 19 | const ColorSwatch = styled.View<{ color: string }>(({ color, theme }) => ({ 20 | height: 40, 21 | width: '100%', 22 | borderRadius: 4, 23 | backgroundColor: color, 24 | borderColor: theme.appBorderColor, 25 | borderWidth: 1, 26 | })); 27 | 28 | const ValueContainer = styled.View(() => ({ 29 | flex: 1, 30 | flexDirection: 'row', 31 | justifyContent: 'space-between', 32 | padding: 4, 33 | paddingBottom: 0, 34 | })); 35 | 36 | const NameText = styled.Text(({ theme }) => ({ 37 | fontSize: theme.typography.size.s2, 38 | color: theme.color.defaultText, 39 | fontWeight: theme.typography.weight.bold, 40 | })); 41 | 42 | const ValueText = styled.Text(({ theme }) => ({ 43 | fontSize: theme.typography.size.s2, 44 | color: theme.color.defaultText, 45 | })); 46 | 47 | const Swatch = ({ name, value, setBackground }: SwatchProps) => ( 48 | setBackground(value)}> 49 | 50 | 51 | {name} 52 | {value} 53 | 54 | 55 | ); 56 | 57 | export default Swatch; 58 | -------------------------------------------------------------------------------- /packages/ondevice-controls/src/types/Array.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { useResyncValue } from './useResyncValue'; 3 | import { Input } from './common'; 4 | 5 | function formatArray(value: string, separator: string) { 6 | if (value === '') { 7 | return []; 8 | } 9 | return value.split(separator); 10 | } 11 | 12 | export interface ArrayProps { 13 | arg: { 14 | name: string; 15 | value: string[]; 16 | separator: string; 17 | }; 18 | onChange: (value: string[]) => void; 19 | isPristine: boolean; 20 | } 21 | 22 | const ArrayType = ({ 23 | arg: { name, value, separator = ',' }, 24 | onChange = () => null, 25 | isPristine, 26 | }: ArrayProps) => { 27 | const { setCurrentValue, key } = useResyncValue(value, isPristine); 28 | 29 | const [focused, setFocused] = useState(false); 30 | 31 | return ( 32 | { 39 | const formatted = formatArray(text, separator); 40 | onChange(formatted); 41 | setCurrentValue(formatted); 42 | }} 43 | onFocus={() => setFocused(true)} 44 | onBlur={() => setFocused(false)} 45 | focused={focused} 46 | /> 47 | ); 48 | }; 49 | 50 | ArrayType.serialize = (value) => value; 51 | 52 | ArrayType.deserialize = (value) => { 53 | if (Array.isArray(value)) { 54 | return value; 55 | } 56 | 57 | return Object.keys(value) 58 | .sort() 59 | .reduce((array, key) => [...array, value[key]], []); 60 | }; 61 | 62 | export default ArrayType; 63 | --------------------------------------------------------------------------------
{siteConfig.tagline}