Skip to content

Commit

Permalink
feat: re-add knobs to provide compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
dannyhw committed Nov 27, 2022
1 parent 60dcfbf commit 5917356
Show file tree
Hide file tree
Showing 36 changed files with 2,431 additions and 10 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ examples/native/index.html
storybook.requires.js
jest.config.js
app/react-native/scripts/mocks
addons/ondevice-knobs
2 changes: 1 addition & 1 deletion addons/ondevice-controls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ The [web Controls Addon documentation](https://storybook.js.org/docs/react/essen

## Migrating from Knobs

See [examples for migrating from Knobs to Controls](https://github.com/storybookjs/storybook/tree/next/addons/controls#how-do-i-migrate-from-addon-knobs) in the web Controls Addon README.
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.
30 changes: 30 additions & 0 deletions addons/ondevice-knobs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Storybook Knobs Addon for react-native

This addon is depcrecated in favour of controls, please only use it for transitioning to CSF, Args and Controls

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.

Storybook Knobs Addon allows you to edit react props using the Storybook UI using variables inside stories in [Storybook](https://storybook.js.org).

[Framework Support](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)

**This is a wrapper for the addon @storybook/addon-knobs Refer to its documentation to understand how to use knobs**

## Installation

```sh
yarn add -D @storybook/addon-ondevice-knobs @storybook/addon-knobs @react-native-community/datetimepicker @react-native-community/slider
```

## Configuration

Add following content to `.storybook/main.js`:

```js
module.exports = {
addons: ['@storybook/addon-ondevice-knobs'],
};
```

Make sure to use the withKnobs decorator when using knobs `import { withKnobs } from '@storybook/addon-ondevice-knobs';`

54 changes: 54 additions & 0 deletions addons/ondevice-knobs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@storybook/addon-ondevice-knobs",
"version": "6.0.1-beta.10",
"description": "Display storybook story knobs on your deviced.",
"keywords": [
"addon",
"knobs",
"ondevice",
"react-native",
"storybook"
],
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/react-native.git",
"directory": "addons/ondevice-knobs"
},
"license": "MIT",
"main": "dist/index.js",
"files": [
"dist/**/*",
"docs/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@emotion/native": "^10.0.14",
"@storybook/addons": "^6.5.3",
"@storybook/core-events": "^6.5.3",
"core-js": "^3.0.1",
"deep-equal": "^1.0.1",
"prop-types": "^15.7.2",
"react-native-modal-datetime-picker": "^14.0.0",
"react-native-modal-selector": "^2.1.1",
"tinycolor2": "^1.4.1"
},
"devDependencies": {
"@storybook/addon-knobs": "^6"
},
"peerDependencies": {
"@react-native-community/datetimepicker": "*",
"@react-native-community/slider": "*",
"@storybook/addon-knobs": "^6",
"react": "*",
"react-native": "*"
},
"publishConfig": {
"access": "public"
},
"gitHead": "4b9d901add9452525135caae98ae5f78dd8da9ff"
}
1 change: 1 addition & 0 deletions addons/ondevice-knobs/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('./dist/index').register();
72 changes: 72 additions & 0 deletions addons/ondevice-knobs/src/GroupTabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { ScrollView, Text, TouchableOpacity } from 'react-native';
import styled from '@emotion/native';

const Label = styled.Text(({ theme, active }) => ({
color: active ? theme.buttonActiveTextColor || '#444444' : theme.buttonTextColor || '#999999',
fontSize: 17,
}));

class GroupTabs extends Component {
renderTab(name, group) {
let { title } = group;
if (typeof title === 'function') {
title = title();
}

const { onGroupSelect, selectedGroup } = this.props;

return (
<TouchableOpacity
style={{
marginTop: 5,
marginRight: 15,
paddingBottom: 10,
}}
key={name}
onPress={() => onGroupSelect(name)}
>
<Label active={selectedGroup === name}>{title}</Label>
</TouchableOpacity>
);
}

render() {
const { groups } = this.props;

const entries = groups ? Object.entries(groups) : null;

return entries && entries.length ? (
<ScrollView
horizontal
style={{
marginHorizontal: 10,
flexDirection: 'row',
marginBottom: 10,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
}}
>
{entries.map(([key, value]) => this.renderTab(key, value))}
</ScrollView>
) : (
<Text>no groups available</Text>
);
}
}

GroupTabs.defaultProps = {
groups: {},
onGroupSelect: () => {},
selectedGroup: null,
};

GroupTabs.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
groups: PropTypes.object,
onGroupSelect: PropTypes.func,
selectedGroup: PropTypes.string,
};

export default GroupTabs;
50 changes: 50 additions & 0 deletions addons/ondevice-knobs/src/PropField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import React from 'react';
import styled from '@emotion/native';
import TypeMap from './types';

const InvalidType = () => <Text style={{ margin: 10 }}>Invalid Type</Text>;

const Label = styled.Text(({ theme }) => ({
marginLeft: 10,
fontSize: 14,
color: theme.labelColor || 'black',
fontWeight: 'bold',
}));

const PropField = ({ onChange, onPress, knob }) => {
const InputType = TypeMap[knob.type] || InvalidType;

return (
<View>
{!knob.hideLabel ? <Label>{`${knob.label || knob.name}`}</Label> : null}
<InputType knob={knob} onChange={onChange} onPress={onPress} />
</View>
);
};

PropField.propTypes = {
knob: PropTypes.shape({
name: PropTypes.string,
label: PropTypes.string,
value: PropTypes.any,
hideLabel: PropTypes.bool,
type: PropTypes.oneOf([
'text',
'number',
'color',
'boolean',
'object',
'select',
'array',
'date',
'button',
'radios',
]),
}).isRequired,
onChange: PropTypes.func.isRequired,
onPress: PropTypes.func.isRequired,
};

export default PropField;
56 changes: 56 additions & 0 deletions addons/ondevice-knobs/src/PropForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint no-underscore-dangle: 0 */

import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import PropField from './PropField';

export default class PropForm extends React.Component {
makeChangeHandler(name, type) {
return (value) => {
const { onFieldChange } = this.props;
const change = { name, type, value };
onFieldChange(change);
};
}

render() {
const { knobs, onFieldClick } = this.props;

return (
<View>
{knobs.map((knob) => {
const changeHandler = this.makeChangeHandler(knob.name, knob.type);
return (
<PropField
key={knob.name}
name={knob.name}
type={knob.type}
value={knob.value}
knob={knob}
onChange={changeHandler}
onPress={onFieldClick}
/>
);
})}
</View>
);
}
}

PropForm.displayName = 'PropForm';

PropForm.defaultProps = {
knobs: [],
};

PropForm.propTypes = {
knobs: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
value: PropTypes.any,
})
),
onFieldChange: PropTypes.func.isRequired,
onFieldClick: PropTypes.func.isRequired,
};
91 changes: 91 additions & 0 deletions addons/ondevice-knobs/src/components/RadioSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import styled from '@emotion/native';
import PropTypes from 'prop-types';
import React from 'react';
import { StyleSheet } from 'react-native';

const RadioContainer = styled.View(({ isInline }) => ({
flexDirection: isInline ? 'row' : 'column',
alignItems: isInline ? 'center' : 'flex-start',
flexWrap: 'wrap',
margin: 10,
}));

const RadioTouchable = styled.TouchableOpacity(() => ({
marginRight: 8,
alignItems: 'center',
flexDirection: 'row',
padding: 4,
}));

const RadioCircle = styled.View(({ theme }) => ({
width: 16,
height: 16,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
marginRight: 4,
borderWidth: StyleSheet.hairlineWidth,
borderColor: theme.borderColor || '#e6e6e6',
}));

const RadioInnerCircle = styled.View(({ selected }) => ({
position: 'absolute',
height: 12,
width: 12,
borderRadius: 6,
backgroundColor: selected ? '#66bf3c' : 'transparent',
}));

const RadioLabel = styled.Text(() => ({
fontSize: 13,
}));

class RadioSelect extends React.Component {
constructor(props) {
super(props);

const { initValue } = this.props;

this.state = {
value: initValue,
};
}

render() {
const { data, onChange } = this.props;
const { value } = this.state;
return (
<RadioContainer>
{data.map((item) => (
<RadioTouchable
key={item.label}
activeOpacity={0.7}
onPress={() => {
onChange(item);
this.setState({ value: item.key });
}}
>
<RadioCircle>
<RadioInnerCircle selected={value === item.key} />
</RadioCircle>
<RadioLabel>{item.label}</RadioLabel>
</RadioTouchable>
))}
</RadioContainer>
);
}
}

export default RadioSelect;

RadioSelect.defaultProps = {
data: [],
onChange: (value) => value,
initValue: '',
};

RadioSelect.propTypes = {
data: PropTypes.arrayOf(PropTypes.object),
initValue: PropTypes.string,
onChange: PropTypes.func,
};
Loading

0 comments on commit 5917356

Please sign in to comment.