Skip to content

Commit f11af28

Browse files
committed
Add Storybook addon and options to useStyle
1 parent bb67607 commit f11af28

File tree

6 files changed

+123
-14
lines changed

6 files changed

+123
-14
lines changed

README.md

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ A package that allows you to create React Native StyleSheets with support for Da
99
- Simple API
1010
- Fully typed
1111
- Builds on top of StyleSheets and Hooks
12+
- Storybook addon to change Theme Mode
1213

1314
## Installation
1415

@@ -64,12 +65,12 @@ import { View, Text, Button } from 'react-native'
6465
import { ThemeProvider, useStyle, useTheme } from './theme'
6566

6667
const ComponentWithUseStyle: React.FC = () => {
67-
const styles = useStyle(theme => {
68+
const styles = useStyle((theme, options) => {
6869
text: {
69-
color: theme.textColor,
70+
color: options.disabled ? '#C9C9C9' : theme.textColor,
7071
fontSize: theme.fontSize
7172
}
72-
})
73+
}, { disabled: true }) // Options is optional
7374

7475
return (
7576
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
@@ -106,10 +107,39 @@ const ComponentWithUseTheme: React.FC = () => {
106107
)
107108
}
108109
```
110+
## Storybook Addon
111+
112+
### Installation:
113+
114+
```js
115+
// storybook.js
116+
import {
117+
getStorybookUI,
118+
configure,
119+
addDecorator,
120+
addParameters
121+
} from '@storybook/react-native'
122+
import { withThemeHook } from 'react-native-themed-stylesheet/storybook'
123+
import 'react-native-themed-stylesheet/storybook/register'
124+
import { useTheme } from './theme'
125+
126+
addDecorator(withThemeHook)
127+
addParameters({
128+
useTheme
129+
})
130+
131+
configure(() => {
132+
require('path/to/some/story')
133+
}, module)
134+
135+
const StorybookUIRoot = getStorybookUI()
136+
137+
export default StorybookUIRoot // Make sure to use this component within ThemeProvider.
138+
```
109139

110140
## API
111141

112-
### Function: `createTheme(themes, initialMode)`
142+
### Function: `createTheme(themes, [initialMode])`
113143

114144
Use this function to create the theme.
115145

@@ -144,13 +174,14 @@ A react component to provide ThemeContext.
144174

145175
---
146176

147-
### Function: `useStyle(createStyle)`
177+
### Function: `useStyle(createStyles, [options])`
148178

149179
Hook to create themed stylesheets.
150180

151181
**Parameters**
152182

153-
- `createStyle`: A function that receives the current theme and returns an object of type `T`.
183+
- `createStyles`: A function that receives the current theme and options and returns an object of type `T`.
184+
- `options`: Custom options to be use inside createStyles.
154185

155186
**Returns**
156187

index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ type UseTheme<T, C> = () => ThemeContext<T, C>
2525
type ThemeObject = {
2626
[props: string]: ViewStyle | TextStyle | ImageStyle
2727
}
28-
type UseStyle<T, C> = <S extends ThemeObject>(
29-
createStyleSheet: (theme: T & C) => S
28+
type UseStyle<T, C> = <S extends ThemeObject, O>(
29+
createStyleSheet: (theme: T & C, options?: O) => S,
30+
options?: O
3031
) => StyleSheet.NamedStyles<S>
3132
type CreateTheme = <T, C>(
3233
themes: Themes<T, C | undefined>,
@@ -107,9 +108,9 @@ const createTheme: CreateTheme = (ts, initialMode = 'auto') => {
107108
)
108109

109110
const useTheme: UseTheme<Theme, Common> = () => useContext(ThemeContext)
110-
const useStyle: UseStyle<Theme, Common> = createStyledObject => {
111+
const useStyle: UseStyle<Theme, Common> = (createStyledObject, options) => {
111112
const { theme } = useTheme()
112-
return StyleSheet.create(createStyledObject(theme))
113+
return StyleSheet.create(createStyledObject(theme, options))
113114
}
114115

115116
return { ThemeProvider, useStyle, useTheme }

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-themed-stylesheet",
3-
"version": "0.1.0",
3+
"version": "0.2.1",
44
"description": "React Native StyleSheets with Theming Support",
55
"author": "Andre Pedroza",
66
"license": "MIT",
@@ -10,7 +10,7 @@
1010
},
1111
"main": "index.js",
1212
"types": "index.d.ts",
13-
"files": ["index.js", "index.d.ts"],
13+
"files": ["index.js", "index.d.ts", "storybook"],
1414
"scripts": {
1515
"build": "tsc",
1616
"test": "jest",
@@ -20,7 +20,8 @@
2020
"peerDependencies": {
2121
"react": "*",
2222
"react-native": "*",
23-
"react-native-appearance": "*"
23+
"react-native-appearance": "*",
24+
"@storybook/addons": "*"
2425
},
2526
"devDependencies": {
2627
"@babel/core": "^7.12.16",

storybook/register.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* eslint-disable multiline-ternary */
2+
import React, { useEffect, useState } from 'react'
3+
import { Text, TouchableOpacity, View } from 'react-native'
4+
import addons from '@storybook/addons'
5+
6+
const ADDON_ID = 'storybook-addon-ondevice-themed-stylesheet'
7+
const PANEL_ID = `${ADDON_ID}/panel`
8+
9+
const Button = ({ mode, active, setMode }) => (
10+
<TouchableOpacity
11+
style={{
12+
borderRadius: 5,
13+
borderWidth: 1,
14+
borderColor: 'rgba(0,0,0,0.5)',
15+
padding: 10,
16+
marginHorizontal: 10,
17+
marginVertical: 5,
18+
backgroundColor: active ? 'black' : 'white'
19+
}}
20+
onPress={() => setMode(mode)}
21+
>
22+
<Text style={{ textAlign: 'center', color: active ? 'white' : 'black' }}>
23+
{mode.toUpperCase()}
24+
</Text>
25+
</TouchableOpacity>
26+
)
27+
28+
const ThemePanel = ({ channel }) => {
29+
const [currentMode, setCurrentMode] = useState(null)
30+
useEffect(() => {
31+
const onModeChange = newMode => setCurrentMode(newMode)
32+
channel.on('mode', onModeChange)
33+
return channel.removeListener(onModeChange)
34+
}, [])
35+
useEffect(() => {
36+
currentMode && channel.emit('setMode', currentMode)
37+
}, [currentMode])
38+
return currentMode ? (
39+
<View>
40+
{['auto', 'light', 'dark'].map(m => (
41+
<View key={m}>
42+
<Button
43+
mode={m}
44+
active={currentMode === m}
45+
setMode={setCurrentMode}
46+
/>
47+
</View>
48+
))}
49+
</View>
50+
) : null
51+
}
52+
53+
addons.register(ADDON_ID, () => {
54+
const channel = addons.getChannel()
55+
addons.addPanel(PANEL_ID, {
56+
title: 'Theme',
57+
render: () => <ThemePanel channel={channel} />
58+
})
59+
})

tests/fixture.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,13 @@ export const style2 = (theme: typeof themes2.light) => ({
3434
color: theme.textColor
3535
}
3636
})
37+
38+
export const style3 = (
39+
theme: typeof themes1.common & typeof themes1.light,
40+
options?: { disabled: boolean }
41+
) => ({
42+
text: {
43+
color: options && options.disabled ? '#c0c0c0' : theme.textColor,
44+
fontSize: theme.fontSize
45+
}
46+
})

tests/tests.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { renderHook, act } from '@testing-library/react-hooks/native'
22
import { Appearance } from '../__mocks__/react-native-appearance'
33
import { createTheme } from '..'
4-
import { style1, style2, themes1, themes2 } from './fixture'
4+
import { style1, style2, style3, themes1, themes2 } from './fixture'
55
import { TextStyle } from 'react-native'
66

77
beforeEach(() => {
@@ -17,6 +17,13 @@ describe('With Common Prop and Auto Mode', () => {
1717
expect((result.current.text as TextStyle).color).toEqual('#ff0000')
1818
expect((result.current.text as TextStyle).fontSize).toEqual(12)
1919
})
20+
test('Create style with useStyle() and options', () => {
21+
const { result } = renderHook(() => useStyle(style3, { disabled: true }), {
22+
wrapper: ThemeProvider
23+
})
24+
expect((result.current.text as TextStyle).color).toEqual('#c0c0c0')
25+
expect((result.current.text as TextStyle).fontSize).toEqual(12)
26+
})
2027
test('Create style with useTheme()', () => {
2128
const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider })
2229
expect(result.current.theme.textColor).toEqual('#ff0000')

0 commit comments

Comments
 (0)