diff --git a/addons/backgrounds/package.json b/addons/backgrounds/package.json
index ec3ccb3da524..dcb89bca0ffa 100644
--- a/addons/backgrounds/package.json
+++ b/addons/backgrounds/package.json
@@ -19,7 +19,7 @@
"license": "MIT",
"author": "jbaxleyiii",
"main": "dist/index.js",
- "jsnext:main": "src/index.js",
+ "types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
@@ -30,13 +30,14 @@
"@storybook/core-events": "5.0.0-rc.4",
"@storybook/theming": "5.0.0-rc.4",
"core-js": "^2.6.2",
- "eventemitter3": "^3.1.0",
"global": "^4.3.2",
"memoizerific": "^1.11.3",
- "prop-types": "^15.6.2",
"react": "^16.8.1",
"util-deprecate": "^1.0.2"
},
+ "devDependencies": {
+ "@types/util-deprecate": "^1.0.0"
+ },
"publishConfig": {
"access": "public"
}
diff --git a/addons/backgrounds/src/Tool.js b/addons/backgrounds/src/Tool.js
deleted file mode 100644
index d05a7658936d..000000000000
--- a/addons/backgrounds/src/Tool.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import React, { Component, Fragment } from 'react';
-import PropTypes from 'prop-types';
-import memoize from 'memoizerific';
-
-import { Global, styled } from '@storybook/theming';
-
-import { SET_STORIES } from '@storybook/core-events';
-
-import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
-
-import { PARAM_KEY } from './constants';
-
-export const ColorIcon = styled.span(({ background }) => ({
- borderRadius: '1rem',
- display: 'block',
- height: '1rem',
- width: '1rem',
- background,
-}));
-
-const iframeId = 'storybook-preview-background';
-
-const createItem = memoize(1000)((id, name, value, hasSwatch, change) =>
- hasSwatch
- ? {
- id: id || name,
- title: name,
- onClick: () => {
- change({ selected: value, expanded: false });
- },
- value,
- right: ,
- }
- : {
- id: id || name,
- title: name,
- onClick: () => {
- change({ selected: value, expanded: false });
- },
- value,
- }
-);
-
-const getSelected = (list, state) => {
- if (!list.length) {
- return 'transparent';
- }
-
- if (state === 'transparent') {
- return state;
- }
-
- if (list.find(i => i.value === state)) {
- return state;
- }
-
- if (list.find(i => i.default)) {
- return list.find(i => i.default).value;
- }
-
- return 'transparent';
-};
-
-const getState = memoize(10)((props, state, change) => {
- const data = props.api.getCurrentStoryData();
- const list = (data && data.parameters && data.parameters[PARAM_KEY]) || [];
-
- const selected = getSelected(list, state.selected);
-
- const initial =
- selected !== 'transparent'
- ? [createItem('reset', 'Clear background', 'transparent', false, change)]
- : [];
-
- const items = list.length
- ? initial.concat(list.map(({ id, name, value }) => createItem(id, name, value, true, change)))
- : list;
-
- return {
- items,
- selected,
- };
-});
-
-export default class BackgroundTool extends Component {
- static propTypes = {
- api: PropTypes.shape({
- getQueryParam: PropTypes.func,
- setQueryParams: PropTypes.func,
- }).isRequired,
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- items: [],
- selected: undefined,
- expanded: false,
- };
-
- this.listener = () => {
- this.setState({ selected: null });
- };
- }
-
- componentDidMount() {
- const { api } = this.props;
- api.on(SET_STORIES, this.listener);
- }
-
- componentWillUnmount() {
- const { api } = this.props;
- api.off(SET_STORIES, this.listener);
- }
-
- change = (...args) => this.setState(...args);
-
- render() {
- const { expanded } = this.state;
- const { items, selected } = getState(this.props, this.state, this.change);
-
- return items.length ? (
-
- {selected ? (
-
- ) : null}
- this.setState({ expanded: s })}
- tooltip={}
- closeOnClick
- >
-
-
-
-
-
- ) : null;
- }
-}
diff --git a/addons/backgrounds/src/components.js b/addons/backgrounds/src/components.js
deleted file mode 100644
index 9bc6443aaf80..000000000000
--- a/addons/backgrounds/src/components.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { styled } from '@storybook/theming';
-
-export const ColorIcon = styled.span(({ background }) => ({
- background,
-}));
diff --git a/addons/backgrounds/src/components/ColorIcon.tsx b/addons/backgrounds/src/components/ColorIcon.tsx
new file mode 100644
index 000000000000..aabdce3ffd46
--- /dev/null
+++ b/addons/backgrounds/src/components/ColorIcon.tsx
@@ -0,0 +1,9 @@
+import { styled } from '@storybook/theming';
+
+export const ColorIcon = styled.span(({ background }: { background: string }) => ({
+ borderRadius: '1rem',
+ display: 'block',
+ height: '1rem',
+ width: '1rem',
+ background,
+}));
diff --git a/addons/backgrounds/src/components/index.ts b/addons/backgrounds/src/components/index.ts
new file mode 100644
index 000000000000..db2a966bc4fe
--- /dev/null
+++ b/addons/backgrounds/src/components/index.ts
@@ -0,0 +1 @@
+export * from './ColorIcon';
diff --git a/addons/backgrounds/src/constants.js b/addons/backgrounds/src/constants.ts
similarity index 86%
rename from addons/backgrounds/src/constants.js
rename to addons/backgrounds/src/constants.ts
index 39f865027c50..df63c53889d1 100644
--- a/addons/backgrounds/src/constants.js
+++ b/addons/backgrounds/src/constants.ts
@@ -1,7 +1,7 @@
export const ADDON_ID = 'storybook/background';
export const PARAM_KEY = 'backgrounds';
-export default {
+export const EVENTS = {
SET: `${ADDON_ID}:set`,
UNSET: `${ADDON_ID}:unset`,
};
diff --git a/addons/backgrounds/src/containers/BackgroundSelector.tsx b/addons/backgrounds/src/containers/BackgroundSelector.tsx
new file mode 100644
index 000000000000..9dd602b9a72e
--- /dev/null
+++ b/addons/backgrounds/src/containers/BackgroundSelector.tsx
@@ -0,0 +1,165 @@
+import React, { Component, Fragment } from 'react';
+import memoize from 'memoizerific';
+
+import { Global } from '@storybook/theming';
+
+import { SET_STORIES } from '@storybook/core-events';
+
+import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
+
+import { PARAM_KEY } from '../constants';
+import { ColorIcon } from '../components';
+import { BackgroundConfig, BackgroundSelectorItem } from '../models';
+
+const iframeId = 'storybook-preview-background';
+
+const createBackgroundSelectorItem = memoize(1000)(
+ (
+ id: string,
+ name: string,
+ value: string,
+ hasSwatch: boolean,
+ change: (arg: { selected: string; expanded: boolean }) => void
+ ): BackgroundSelectorItem => ({
+ id: id || name,
+ title: name,
+ onClick: () => {
+ change({ selected: value, expanded: false });
+ },
+ value,
+ right: hasSwatch ? : undefined,
+ })
+);
+
+const getSelectedBackgroundColor = (
+ list: BackgroundConfig[],
+ currentSelectedValue: string
+): string => {
+ if (!list.length) {
+ return 'transparent';
+ }
+
+ if (currentSelectedValue === 'transparent') {
+ return currentSelectedValue;
+ }
+
+ if (list.find(i => i.value === currentSelectedValue)) {
+ return currentSelectedValue;
+ }
+
+ if (list.find(i => i.default)) {
+ return list.find(i => i.default).value;
+ }
+
+ return 'transparent';
+};
+
+const getDisplayableState = memoize(10)(
+ (props: BackgroundToolProps, state: BackgroundToolState, change) => {
+ const data = props.api.getCurrentStoryData();
+ const list: BackgroundConfig[] = (data && data.parameters && data.parameters[PARAM_KEY]) || [];
+
+ const selectedBackgroundColor = getSelectedBackgroundColor(list, state.selected);
+
+ let availableBackgroundSelectorItems: BackgroundSelectorItem[] = [];
+
+ if (selectedBackgroundColor !== 'transparent') {
+ availableBackgroundSelectorItems.push(
+ createBackgroundSelectorItem('reset', 'Clear background', 'transparent', false, change)
+ );
+ }
+
+ if (list.length) {
+ availableBackgroundSelectorItems = [
+ ...availableBackgroundSelectorItems,
+ ...list.map(({ name, value }) =>
+ createBackgroundSelectorItem(null, name, value, true, change)
+ ),
+ ];
+ }
+
+ return {
+ items: availableBackgroundSelectorItems,
+ selectedBackgroundColor,
+ };
+ }
+);
+
+interface BackgroundToolProps {
+ api: {
+ on(event: string, callback: (data: any) => void): void;
+ off(event: string, callback: (data: any) => void): void;
+ getCurrentStoryData(): any;
+ };
+}
+
+interface BackgroundToolState {
+ items: BackgroundSelectorItem[];
+ selected: string;
+ expanded: boolean;
+}
+
+export class BackgroundSelector extends Component {
+ private listener = () => {
+ this.setState({ selected: null });
+ };
+
+ constructor(props: BackgroundToolProps) {
+ super(props);
+
+ this.state = {
+ items: [],
+ selected: null,
+ expanded: false,
+ };
+ }
+
+ componentDidMount() {
+ const { api } = this.props;
+ api.on(SET_STORIES, this.listener);
+ }
+
+ componentWillUnmount() {
+ const { api } = this.props;
+ api.off(SET_STORIES, this.listener);
+ }
+
+ change = (args: { selected: string; expanded: boolean }) => this.setState(args);
+
+ render() {
+ const { expanded } = this.state;
+ const { items, selectedBackgroundColor } = getDisplayableState(
+ this.props,
+ this.state,
+ this.change
+ );
+
+ return items.length ? (
+
+ {selectedBackgroundColor ? (
+
+ ) : null}
+
+ this.setState({ expanded: newVisibility })
+ }
+ tooltip={}
+ closeOnClick
+ >
+
+
+
+
+
+ ) : null;
+ }
+}
diff --git a/addons/backgrounds/src/containers/index.ts b/addons/backgrounds/src/containers/index.ts
new file mode 100644
index 000000000000..87f68e7c7ae1
--- /dev/null
+++ b/addons/backgrounds/src/containers/index.ts
@@ -0,0 +1 @@
+export * from './BackgroundSelector';
diff --git a/addons/backgrounds/src/deprecated.js b/addons/backgrounds/src/deprecated.ts
similarity index 100%
rename from addons/backgrounds/src/deprecated.js
rename to addons/backgrounds/src/deprecated.ts
diff --git a/addons/backgrounds/src/index.js b/addons/backgrounds/src/index.ts
similarity index 68%
rename from addons/backgrounds/src/index.js
rename to addons/backgrounds/src/index.ts
index 460a270e44b9..7bccd015db8f 100644
--- a/addons/backgrounds/src/index.js
+++ b/addons/backgrounds/src/index.ts
@@ -1,14 +1,15 @@
-import addons, { makeDecorator } from '@storybook/addons';
-import CoreEvents from '@storybook/core-events';
+import { addons, makeDecorator } from '@storybook/addons';
import deprecate from 'util-deprecate';
-import Events from './constants';
+import { REGISTER_SUBSCRIPTION } from '@storybook/core-events';
+import { EVENTS } from './constants';
+import { BackgroundConfig } from './models';
-let prevBackgrounds;
+let prevBackgrounds: BackgroundConfig[];
const subscription = () => () => {
prevBackgrounds = null;
- addons.getChannel().emit(Events.UNSET);
+ addons.getChannel().emit(EVENTS.UNSET);
};
export const withBackgrounds = makeDecorator({
@@ -25,10 +26,10 @@ export const withBackgrounds = makeDecorator({
}
if (prevBackgrounds !== backgrounds) {
- addons.getChannel().emit(Events.SET, backgrounds);
+ addons.getChannel().emit(EVENTS.SET, backgrounds);
prevBackgrounds = backgrounds;
}
- addons.getChannel().emit(CoreEvents.REGISTER_SUBSCRIPTION, subscription);
+ addons.getChannel().emit(REGISTER_SUBSCRIPTION, subscription);
return getStory(context);
},
diff --git a/addons/backgrounds/src/models/BackgroundConfig.ts b/addons/backgrounds/src/models/BackgroundConfig.ts
new file mode 100644
index 000000000000..f48826281bd5
--- /dev/null
+++ b/addons/backgrounds/src/models/BackgroundConfig.ts
@@ -0,0 +1,5 @@
+export interface BackgroundConfig {
+ name: string;
+ value: string;
+ default?: boolean;
+}
diff --git a/addons/backgrounds/src/models/BackgroundSelectorItem.ts b/addons/backgrounds/src/models/BackgroundSelectorItem.ts
new file mode 100644
index 000000000000..fdc35e55c10a
--- /dev/null
+++ b/addons/backgrounds/src/models/BackgroundSelectorItem.ts
@@ -0,0 +1,7 @@
+export interface BackgroundSelectorItem {
+ id: string;
+ title: string;
+ onClick: () => void;
+ value: string;
+ right?: any;
+}
diff --git a/addons/backgrounds/src/models/index.ts b/addons/backgrounds/src/models/index.ts
new file mode 100644
index 000000000000..dc3676a463d3
--- /dev/null
+++ b/addons/backgrounds/src/models/index.ts
@@ -0,0 +1,2 @@
+export * from './BackgroundConfig';
+export * from './BackgroundSelectorItem';
diff --git a/addons/backgrounds/src/register.js b/addons/backgrounds/src/register.tsx
similarity index 54%
rename from addons/backgrounds/src/register.js
rename to addons/backgrounds/src/register.tsx
index 7f398493438a..d592fdd43ef0 100644
--- a/addons/backgrounds/src/register.js
+++ b/addons/backgrounds/src/register.tsx
@@ -1,13 +1,14 @@
import React from 'react';
-import addons, { types } from '@storybook/addons';
+import { addons, types } from '@storybook/addons';
import { ADDON_ID } from './constants';
-import Tool from './Tool';
+import { BackgroundSelector } from './containers';
addons.register(ADDON_ID, api => {
addons.add(ADDON_ID, {
+ title: 'Backgrounds',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
- render: () => ,
+ render: () => ,
});
});
diff --git a/addons/backgrounds/src/typings.d.ts b/addons/backgrounds/src/typings.d.ts
new file mode 100644
index 000000000000..0ca9d4552932
--- /dev/null
+++ b/addons/backgrounds/src/typings.d.ts
@@ -0,0 +1,2 @@
+// TODO: following packages need definition files or a TS migration
+declare module '@storybook/components';
diff --git a/addons/backgrounds/tsconfig.json b/addons/backgrounds/tsconfig.json
new file mode 100644
index 000000000000..8876bb6737a1
--- /dev/null
+++ b/addons/backgrounds/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "./src",
+ "types": ["webpack-env"]
+ },
+ "include": [
+ "src/**/*"
+ ],
+ "exclude": [
+ "src/__tests__/**/*"
+ ]
+}