Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate @storybook/addon-events to Typescript #7190

Merged
merged 18 commits into from
Jul 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion addons/events/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
},
"license": "MIT",
"main": "dist/index.js",
"jsnext:main": "src/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/api": "5.2.0-alpha.35",
"@storybook/addons": "5.2.0-alpha.35",
"@storybook/core-events": "5.2.0-alpha.35",
"@storybook/theming": "5.2.0-alpha.35",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ import isEqual from 'lodash/isEqual';

import { styled } from '@storybook/theming';
import json from 'format-json';

import Textarea from 'react-textarea-autosize';
import { OnEmitEvent } from '../index';

interface StyledTextareaProps {
shown: boolean;
failed: boolean;
value?: string;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably I missed something... but putting only ...rest didn't work. I also had to add value, onChange which seems unnecessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use something like React.HTMLProps<HTMLTextAreaElement>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll try that!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm... It didn't work for me. Maybe I implemented in a wrong way?

onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
}

const StyledTextarea = styled(Textarea)(
const StyledTextarea = styled(({ shown, failed, ...rest }: StyledTextareaProps) => (
<Textarea {...rest} />
))(
{
flex: '1 0 0',
boxSizing: 'border-box',
Expand Down Expand Up @@ -67,7 +76,7 @@ const Label = styled.label({
textAlign: 'right',
width: 100,
fontWeight: '600',
});
} as any);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one also gives me errors and I couldn't resolve it. Error goes like this

Argument of type '() => { display: string; boxSizing: "border-box"; verticalAlign: string; paddingRight: number; paddingTop: number; textAlign: "right"; width: number; fontWeight: "600"; }' is not assignable to parameter of type 'TemplateStringsArray'.
  Type '() => { display: string; boxSizing: "border-box"; verticalAlign: string; paddingRight: number; paddingTop: number; textAlign: "right"; width: number; fontWeight: "600"; }' is missing the following properties from type 'TemplateStringsArray': raw, concat, join, slice, and 16 more.ts(2345)

Changing code like this also didn't work

const Label = styled.label(() => ({
  display: 'table-cell',
  boxSizing: 'border-box',
  verticalAlign: 'top',
  paddingRight: 5,
  paddingTop: 7,
  textAlign: 'right',
  width: 100,
  fontWeight: '600',
}));

Similar code is already used at other parts

const HighlightToggleLabel = styled.label(({ theme }) => ({
cursor: 'pointer',
userSelect: 'none',
marginBottom: '3px',
marginRight: '3px',
color: theme.color.dark,
}));

const Wrapper = styled.label(({ theme }) => ({
display: 'flex',
borderBottom: `1px solid ${theme.appBorderColor}`,
margin: '0 15px',
padding: '8px 0',
'&:last-child': {
marginBottom: '3rem',
},
}));

Here are for more information this, and this


const Wrapper = styled.div({
display: 'flex',
Expand All @@ -77,15 +86,29 @@ const Wrapper = styled.div({
width: '100%',
});

function getJSONFromString(str) {
function getJSONFromString(str: string) {
try {
return JSON.parse(str);
} catch (e) {
return str;
}
}
interface ItemProps {
name: string;
title: string;
onEmit: (event: OnEmitEvent) => void;
payload: unknown;
}

interface ItemState {
isTextAreaShowed: boolean;
failed: boolean;
payload: unknown;
payloadString: string;
prevPayload: unknown;
}

class Item extends Component {
class Item extends Component<ItemProps, ItemState> {
static propTypes = {
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
Expand All @@ -98,12 +121,16 @@ class Item extends Component {
payload: {},
};

state = {
state: ItemState = {
isTextAreaShowed: false,
failed: false,
payload: null,
payloadString: '',
prevPayload: null,
};

onChange = ({ target: { value } }) => {
const newState = {
onChange = ({ target: { value } }: React.ChangeEvent<HTMLTextAreaElement>) => {
const newState: Partial<ItemState> = {
lonyele marked this conversation as resolved.
Show resolved Hide resolved
payloadString: value,
};

Expand All @@ -114,7 +141,7 @@ class Item extends Component {
newState.failed = true;
}

this.setState(newState);
this.setState(state => ({ ...state, ...newState }));
};

onEmitClick = () => {
Expand All @@ -133,7 +160,7 @@ class Item extends Component {
}));
};

static getDerivedStateFromProps = ({ payload }, { prevPayload }) => {
static getDerivedStateFromProps = ({ payload }: ItemProps, { prevPayload }: ItemState) => {
if (!isEqual(payload, prevPayload)) {
const payloadString = json.plain(payload);
const refinedPayload = getJSONFromString(payloadString);
Expand All @@ -150,7 +177,6 @@ class Item extends Component {
render() {
const { title, name } = this.props;
const { failed, isTextAreaShowed, payloadString } = this.state;

return (
<Wrapper>
<Label htmlFor={`addon-event-${name}`}>{title}</Label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { styled } from '@storybook/theming';
import { API } from '@storybook/api';

import { EVENTS } from '../constants';
import Event from './Event';
import { Event as EventType, OnEmitEvent } from '../index';

const Wrapper = styled.div({
width: '100%',
Expand All @@ -13,7 +15,15 @@ const Wrapper = styled.div({
minHeight: '100%',
});

export default class EventsPanel extends Component {
interface EventsPanelProps {
active: boolean;
api: API;
}
interface EventsPanelState {
events: EventType[];
}

export default class EventsPanel extends Component<EventsPanelProps, EventsPanelState> {
static propTypes = {
active: PropTypes.bool.isRequired,
api: PropTypes.shape({
Expand All @@ -23,7 +33,7 @@ export default class EventsPanel extends Component {
}).isRequired,
};

state = {
state: EventsPanelState = {
events: [],
};

Expand All @@ -39,13 +49,12 @@ export default class EventsPanel extends Component {
api.off(EVENTS.ADD, this.onAdd);
}

onAdd = events => {
onAdd = (events: EventType[]) => {
this.setState({ events });
};

onEmit = event => {
onEmit = (event: OnEmitEvent) => {
const { api } = this.props;

api.emit(EVENTS.EMIT, event);
};

Expand Down
File renamed without changes.
33 changes: 26 additions & 7 deletions addons/events/src/index.js → addons/events/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { ReactNode } from 'react';

import addons from '@storybook/addons';
import CoreEvents from '@storybook/core-events';
import deprecate from 'util-deprecate';

import { EVENTS } from './constants';

let prevEvents;
let currentEmit;
let prevEvents: Event[];
let currentEmit: (name: string, payload: unknown) => void;

export interface OnEmitEvent {
name: string;
payload: unknown;
}

const onEmit = event => {
const onEmit = (event: OnEmitEvent) => {
currentEmit(event.name, event.payload);
};

Expand All @@ -21,7 +28,7 @@ const subscription = () => {
};
};

const addEvents = ({ emit, events }) => {
const addEvents = ({ emit, events }: Options) => {
if (prevEvents !== events) {
addons.getChannel().emit(EVENTS.ADD, events);
prevEvents = events;
Expand All @@ -30,16 +37,28 @@ const addEvents = ({ emit, events }) => {
addons.getChannel().emit(CoreEvents.REGISTER_SUBSCRIPTION, subscription);
};

const WithEvents = deprecate(({ children, ...options }) => {
export interface Event {
name: string;
title: string;
payload: unknown;
}

interface Options {
children?: ReactNode;
emit: (eventName: string, ...args: any) => void;
events: Event[];
}

const WithEvents = deprecate(({ children, ...options }: Options) => {
addEvents(options);
return children;
}, `<WithEvents> usage is deprecated, use .addDecorator(withEvents({emit, events})) instead`);

export default options => {
export default (options: Options) => {
if (options.children) {
return WithEvents(options);
}
return storyFn => {
return (storyFn: () => ReactNode) => {
addEvents(options);
return storyFn();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function register() {
addons.register(ADDON_ID, api => {
addons.addPanel(PANEL_ID, {
title: 'Events',
// eslint-disable-next-line react/prop-types
render: ({ active, key }) => <Panel key={key} api={api} active={active} />,
paramKey: PARAM_KEY,
});
Expand Down
2 changes: 2 additions & 0 deletions addons/events/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare module 'react-lifecycles-compat';
declare module 'format-json';
13 changes: 13 additions & 0 deletions addons/events/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": ["webpack-env"]
},
"include": [
"src/**/*"
],
"exclude": [
"src/__tests__/**/*"
]
}