diff --git a/addons/links/package.json b/addons/links/package.json index 560f4c287758..dda074afec75 100644 --- a/addons/links/package.json +++ b/addons/links/package.json @@ -17,7 +17,7 @@ }, "license": "MIT", "main": "dist/index.js", - "jsnext:main": "src/index.js", + "types": "dist/index.d.ts", "scripts": { "prepare": "node ../../scripts/prepare.js" }, diff --git a/addons/links/src/constants.js b/addons/links/src/constants.ts similarity index 59% rename from addons/links/src/constants.js rename to addons/links/src/constants.ts index 67a313cb314d..8cd994edd00a 100644 --- a/addons/links/src/constants.js +++ b/addons/links/src/constants.ts @@ -2,4 +2,6 @@ export const ADDON_ID = 'storybook/links'; export default { NAVIGATE: `${ADDON_ID}/navigate`, + REQUEST: `${ADDON_ID}/request`, + RECEIVE: `${ADDON_ID}/receive`, }; diff --git a/addons/links/src/index.js b/addons/links/src/index.ts similarity index 93% rename from addons/links/src/index.js rename to addons/links/src/index.ts index c0f6e6372817..00d9a85684d1 100644 --- a/addons/links/src/index.js +++ b/addons/links/src/index.ts @@ -3,7 +3,7 @@ import { linkTo, hrefTo, withLinks } from './preview'; let hasWarned = false; -export function LinkTo() { +export function LinkTo(): null { if (!hasWarned) { // eslint-disable-next-line no-console console.error(stripIndents` diff --git a/addons/links/src/manager.js b/addons/links/src/manager.ts similarity index 100% rename from addons/links/src/manager.js rename to addons/links/src/manager.ts diff --git a/addons/links/src/preview.js b/addons/links/src/preview.ts similarity index 57% rename from addons/links/src/preview.js rename to addons/links/src/preview.ts index f20f7b1621db..8354fdf0e199 100644 --- a/addons/links/src/preview.js +++ b/addons/links/src/preview.ts @@ -1,11 +1,18 @@ +import { SyntheticEvent } from 'react'; import { document } from 'global'; import qs from 'qs'; import addons from '@storybook/addons'; import { SELECT_STORY, STORY_CHANGED } from '@storybook/core-events'; -import { toId } from '@storybook/router/utils'; +import { toId } from '@storybook/router'; -export const navigate = params => addons.getChannel().emit(SELECT_STORY, params); -const generateUrl = id => { +interface Params { + kind: string; + story: string; +} + +export const navigate = (params: Params) => addons.getChannel().emit(SELECT_STORY, params); + +const generateUrl = (id: string) => { const { location } = document; const query = qs.parse(location.search, { ignoreQueryPrefix: true }); return `${location.origin + location.pathname}?${qs.stringify( @@ -14,9 +21,10 @@ const generateUrl = id => { )}`; }; -const valueOrCall = args => value => (typeof value === 'function' ? value(...args) : value); +const valueOrCall = (args: string[]) => (value: string | ((...args: string[]) => string)) => + typeof value === 'function' ? value(...args) : value; -export const linkTo = (kind, story) => (...args) => { +export const linkTo = (kind: string, story?: string) => (...args: string[]) => { const resolver = valueOrCall(args); navigate({ kind: resolver(kind), @@ -24,13 +32,13 @@ export const linkTo = (kind, story) => (...args) => { }); }; -export const hrefTo = (kind, name) => +export const hrefTo = (kind: string, name: string): Promise => new Promise(resolve => { resolve(generateUrl(toId(kind, name))); }); -const linksListener = e => { - const { sbKind: kind, sbStory: story } = e.target.dataset; +const linksListener = (e: SyntheticEvent) => { + const { sbKind: kind, sbStory: story } = e.currentTarget.dataset; if (kind || story) { e.preventDefault(); navigate({ kind, story }); @@ -52,7 +60,7 @@ const off = () => { } }; -export const withLinks = storyFn => { +export const withLinks = (storyFn: () => void) => { on(); addons.getChannel().once(STORY_CHANGED, off); return storyFn(); diff --git a/addons/links/src/react/components/link.test.js b/addons/links/src/react/components/link.test.js index 804dad07e130..2488071651d3 100644 --- a/addons/links/src/react/components/link.test.js +++ b/addons/links/src/react/components/link.test.js @@ -4,7 +4,7 @@ import addons from '@storybook/addons'; import { SELECT_STORY } from '@storybook/core-events'; import { mockChannel } from '../../preview.test'; -import LinkTo from './link'; +import LinkTo from './link.tsx'; jest.mock('@storybook/addons'); diff --git a/addons/links/src/react/components/link.js b/addons/links/src/react/components/link.tsx similarity index 64% rename from addons/links/src/react/components/link.js rename to addons/links/src/react/components/link.tsx index 4c43d8e9ece4..b84547609a87 100644 --- a/addons/links/src/react/components/link.js +++ b/addons/links/src/react/components/link.tsx @@ -1,5 +1,4 @@ import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; import { navigate, hrefTo } from '../../preview'; @@ -9,32 +8,42 @@ import { navigate, hrefTo } from '../../preview'; // Cmd/Ctrl/Shift/Alt + Click should trigger default browser behaviour. Same applies to non-left clicks const LEFT_BUTTON = 0; -const isPlainLeftClick = e => +const isPlainLeftClick = (e: React.MouseEvent) => e.button === LEFT_BUTTON && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey; -const cancelled = (e, cb = () => {}) => { +const cancelled = (e: React.MouseEvent, cb = (_e: any) => {}) => { if (isPlainLeftClick(e)) { e.preventDefault(); cb(e); } }; -export default class LinkTo extends PureComponent { - constructor(...args) { - super(...args); +interface Props { + kind: string; + story: string; + children: React.ReactNode; +} - this.state = { - href: '/', - }; +interface State { + href: string; +} - this.handleClick = this.handleClick.bind(this); - } +export default class LinkTo extends PureComponent { + defaultProps: Props = { + kind: null, + story: null, + children: undefined, + }; + + state: State = { + href: '/', + }; componentDidMount() { this.updateHref(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { const { kind, story } = this.props; if (prevProps.kind !== kind || prevProps.story !== story) { @@ -42,15 +51,15 @@ export default class LinkTo extends PureComponent { } } - async updateHref() { + updateHref = async () => { const { kind, story } = this.props; const href = await hrefTo(kind, story); this.setState({ href }); - } + }; - handleClick() { + handleClick = () => { navigate(this.props); - } + }; render() { const { kind, story, children, ...rest } = this.props; @@ -63,15 +72,3 @@ export default class LinkTo extends PureComponent { ); } } - -LinkTo.defaultProps = { - kind: null, - story: null, - children: undefined, -}; - -LinkTo.propTypes = { - kind: PropTypes.string, - story: PropTypes.string, - children: PropTypes.node, -}; diff --git a/addons/links/src/react/index.js b/addons/links/src/react/index.js deleted file mode 100644 index cb1f5b43c5d0..000000000000 --- a/addons/links/src/react/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './components/link'; diff --git a/addons/links/src/react/index.ts b/addons/links/src/react/index.ts new file mode 100644 index 000000000000..cc4312b98492 --- /dev/null +++ b/addons/links/src/react/index.ts @@ -0,0 +1,2 @@ +import LinkTo from './components/link'; +export default LinkTo; diff --git a/addons/links/src/typings.d.ts b/addons/links/src/typings.d.ts new file mode 100644 index 000000000000..2f4eb9cf4fd9 --- /dev/null +++ b/addons/links/src/typings.d.ts @@ -0,0 +1 @@ +declare module 'global'; diff --git a/addons/links/tsconfig.json b/addons/links/tsconfig.json new file mode 100644 index 000000000000..8876bb6737a1 --- /dev/null +++ b/addons/links/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "types": ["webpack-env"] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "src/__tests__/**/*" + ] +} diff --git a/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js index 7a47470a81eb..684c99c4b9e9 100644 --- a/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js +++ b/addons/storyshots/storyshots-core/src/api/integrityTestTemplate.js @@ -16,7 +16,7 @@ function integrityTest(integrityOptions, stories2snapsConverter) { const possibleStoriesFiles = stories2snapsConverter.getPossibleStoriesFiles(fileName); return !possibleStoriesFiles.some(fs.existsSync); }); - expect(abandonedStoryshots).toHaveLength(0); + expect(abandonedStoryshots.length).toBe(0); }); }); }