├── entry.js
├── register.js
├── preset.js
├── register-options.js
├── .gitignore
├── src
├── preset.ts
├── download-file.ts
├── register.tsx
├── entry.ts
└── components
│ └── sketch-download.tsx
├── .babelrc
├── typings
└── global.d.ts
├── tsconfig.json
├── LICENSE
├── .github
└── workflows
│ └── push.yml
├── README.md
├── package.json
└── CHANGELOG.md
/entry.js:
--------------------------------------------------------------------------------
1 | require('./dist/entry');
2 |
--------------------------------------------------------------------------------
/register.js:
--------------------------------------------------------------------------------
1 | require('./dist/register').default();
2 |
--------------------------------------------------------------------------------
/preset.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/preset');
2 |
--------------------------------------------------------------------------------
/register-options.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/register').default;
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage
3 | .vscode
4 | dist/
5 | yarn-error.log
6 | .env
--------------------------------------------------------------------------------
/src/preset.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | addons: ['storybook-addon-sketch/register'],
3 | entries: ['storybook-addon-sketch/entry']
4 | }
5 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react",
5 | "@babel/preset-typescript"
6 | ],
7 | "plugins": ["@babel/plugin-proposal-class-properties"],
8 | "sourceMaps": true
9 | }
10 |
--------------------------------------------------------------------------------
/src/download-file.ts:
--------------------------------------------------------------------------------
1 | import { saveAs } from 'file-saver';
2 |
3 | const downloadFile = (file: string, json: object) => {
4 | const blob = new Blob([JSON.stringify(json)], {
5 | type: 'text/plain;charset=utf-8'
6 | });
7 |
8 | saveAs(blob, file);
9 | };
10 |
11 | export default downloadFile;
12 |
--------------------------------------------------------------------------------
/typings/global.d.ts:
--------------------------------------------------------------------------------
1 | interface PageSymbol {}
2 |
3 | interface Page {
4 | layers: PageSymbol[];
5 | }
6 |
7 | interface Window {
8 | saveCurrent: (title: string) => void;
9 |
10 | page2layers: {
11 | getPage: (options: {
12 | width: number;
13 | height: number;
14 | title: string;
15 | }) => Page;
16 | getSymbol: (options: {
17 | fixPseudo: boolean;
18 | removePreviewMargin: boolean;
19 | }) => PageSymbol;
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/src/register.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import addons from '@storybook/addons';
3 | import SketchPlugin, { Options } from './components/sketch-download';
4 |
5 | export default (options: Options) =>
6 | addons.register('sketch', api => {
7 | addons.add('sketch/panel', {
8 | type: 'tool',
9 | title: 'Sketch',
10 | match: ({ viewMode }) => viewMode === 'story',
11 | render: () =>
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "baseUrl": ".",
5 | "jsx": "preserve",
6 | "lib": ["dom", "esnext"],
7 | "outDir": "dist",
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 | "resolveJsonModule": true,
12 | "declaration": true,
13 | "noUnusedLocals": true,
14 | "preserveConstEnums": true,
15 | "removeComments": false,
16 | "skipLibCheck": true,
17 | "sourceMap": true,
18 | "strict": true,
19 | "target": "esnext",
20 | "typeRoots": ["./node_modules/@types", "./typings", "typings/markdown.d.ts"]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/entry.ts:
--------------------------------------------------------------------------------
1 | import downloadFile from './download-file';
2 |
3 | window.saveCurrent = (title: string) => {
4 | const page2layers = document.createElement('script');
5 | page2layers.src =
6 | 'https://unpkg.com/story2sketch@1.5.0/lib/browser/page2layers.bundle.js';
7 | page2layers.type = 'text/javascript';
8 | page2layers.onload = () => {
9 | const page = window.page2layers.getPage({
10 | title,
11 | width: 1920,
12 | height: 5000
13 | });
14 |
15 | page.layers = [
16 | window.page2layers.getSymbol({
17 | fixPseudo: true,
18 | removePreviewMargin: true
19 | })
20 | ];
21 |
22 | downloadFile(`${title}.asketch.json`, page);
23 | };
24 |
25 | document.head.appendChild(page2layers);
26 | };
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Andrew Lisowski
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/.github/workflows/push.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | default:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v1
10 | - name: Skip CI
11 | uses: veggiemonk/skip-commit@master
12 | env:
13 | COMMIT_FILTER: skip ci
14 | - name: Prepare repository
15 | env:
16 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
17 | run: |
18 | git checkout "${GITHUB_REF:11}" --
19 | git remote rm origin
20 | git remote add origin "https://$GH_TOKEN@github.com/intuit/storybook-addon-sketch.git"
21 | git fetch origin
22 | git branch --set-upstream-to origin/master
23 | - name: Use Node.js 10.x
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: 10.x
27 | - name: Build
28 | run: |
29 | git remote -v
30 | yarn install --frozen-lockfile
31 | yarn build
32 | - name: Release
33 | env:
34 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
36 | run: |
37 | yarn release
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # storybook-addon-sketch
2 |
3 | > Download sketch files straight from storybook.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | yarn add -D storybook-addon-sketch
9 | # or
10 | npm i --save storybook-addon-sketch
11 | ```
12 |
13 | ## Configuration
14 |
15 | There are two ways you can install this addon.
16 |
17 | ### Preset
18 |
19 | Add the following to your `presets.js`.
20 |
21 | ```js
22 | module.exports = ['storybook-addon-sketch/preset'];
23 | ```
24 |
25 | ### Manually
26 |
27 | Add the following to your `addons.js`.
28 |
29 | ```js
30 | import 'storybook-addon-sketch/register';
31 | ```
32 |
33 | Add the following to your storybook `webpack.config.js`.
34 |
35 | ```js
36 | module.exports = ({ config }) => {
37 | config.entry.push(require.resolve('storybook-addon-sketch/entry'));
38 | };
39 | ```
40 |
41 | And you are all done!
42 |
43 | ## Options
44 |
45 | To configure any options for `storybook-addon-sketch` change your `addons.js` to the following
46 |
47 | ```js
48 | require('storybook-addon-sketch/register-options')(options);
49 | ```
50 |
51 | ### Stories for Kind
52 |
53 | If you want to let your storybook users also download all the default renderings for a story kind (ex: Input).
54 |
55 | ```js
56 | require('storybook-addon-sketch/register-options')({ kind: true });
57 | ```
58 |
59 | You will also need to have ran [story2sketch](https://github.com/chrisvxd/story2sketch) with the option `outputBy` set to `kind` and the `output` set to `out/sketch`. This will output all the sketch files to a specific folder that users can download from.
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "storybook-addon-sketch",
3 | "version": "0.2.1",
4 | "description": "Get the sketch files for your stories in Storybook",
5 | "main": "dist/index.js",
6 | "source": "src/index.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/intuit/storybook-addon-sketch"
10 | },
11 | "files": [
12 | "dist",
13 | "entry.js",
14 | "preset.js",
15 | "register.js",
16 | "register-options.js"
17 | ],
18 | "browserslist": "> 0.25%, not dead",
19 | "author": {
20 | "name": "Andrew Lisowski",
21 | "email": "lisowski54@gmail.com"
22 | },
23 | "publishConfig": {
24 | "registry": "https://registry.npmjs.org"
25 | },
26 | "scripts": {
27 | "build": "npm run build:types && npm run build:js",
28 | "build:watch": "npm run build:js -- --watch --verbose",
29 | "build:js": "babel src --out-dir dist --extensions \".ts,.tsx\"",
30 | "type-check": "tsc --noEmit",
31 | "build:types": "tsc --emitDeclarationOnly",
32 | "release": "auto shipit"
33 | },
34 | "devDependencies": {
35 | "@babel/cli": "^7.4.3",
36 | "@babel/core": "^7.4.3",
37 | "@babel/plugin-proposal-class-properties": "^7.4.0",
38 | "@babel/preset-env": "^7.4.3",
39 | "@babel/preset-react": "^7.0.0",
40 | "@babel/preset-typescript": "^7.3.3",
41 | "@storybook/addons": "^5.2.0",
42 | "@storybook/components": "^5.2.0",
43 | "@types/file-saver": "^2.0.0",
44 | "@types/node": "^12.7.5",
45 | "@types/react": "^16.8.14",
46 | "auto": "^7.6.0",
47 | "husky": "^2.0.0",
48 | "jest": "^24.7.1",
49 | "lint-staged": "^8.1.5",
50 | "prettier": "^1.17.0",
51 | "react": "^16.8.6",
52 | "react-dom": "^16.8.6",
53 | "typescript": "^3.4.5"
54 | },
55 | "peerDependencies": {
56 | "@storybook/addons": ">= 5.x",
57 | "@storybook/components": ">= 5.x",
58 | "react": "*"
59 | },
60 | "dependencies": {
61 | "@types/url-join": "^4.0.0",
62 | "file-saver": "^2.0.1",
63 | "url-join": "^4.0.0"
64 | },
65 | "prettier": {
66 | "singleQuote": true
67 | },
68 | "husky": {
69 | "hooks": {
70 | "pre-commit": "lint-staged"
71 | }
72 | },
73 | "lint-staged": {
74 | "*.{js,json,css,md}": [
75 | "prettier --write",
76 | "git add"
77 | ]
78 | },
79 | "jest": {
80 | "collectCoverage": true,
81 | "coverageDirectory": "coverage",
82 | "coverageReporters": [
83 | "json",
84 | "lcov",
85 | "text",
86 | "html"
87 | ]
88 | },
89 | "license": "MIT",
90 | "auto": {
91 | "plugins": [
92 | "npm",
93 | "released"
94 | ],
95 | "labels": {
96 | "dependency-update": {
97 | "name": "dependency-update",
98 | "title": "Dependency Updates"
99 | }
100 | },
101 | "skipReleaseLabels": [
102 | "dependency-update"
103 | ]
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # v0.2.1 (Fri Jan 15 2021)
2 |
3 | #### 🐛 Bug Fix
4 |
5 | - Fix typo in description [#13](https://github.com/intuit/storybook-addon-sketch/pull/13) ([@kaelig](https://github.com/kaelig))
6 |
7 | #### Authors: 1
8 |
9 | - Kaelig Deloumeau-Prigent ([@kaelig](https://github.com/kaelig))
10 |
11 | ---
12 |
13 | # v0.2.0 (Mon Jun 08 2020)
14 |
15 | #### 🚀 Enhancement
16 |
17 | - simplify preset [#8](https://github.com/intuit/storybook-addon-sketch/pull/8) ([@hipstersmoothie](https://github.com/hipstersmoothie))
18 |
19 | #### Authors: 1
20 |
21 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
22 |
23 | ---
24 |
25 | # v0.1.1 (Mon Jun 08 2020)
26 |
27 | #### 🐛 Bug Fix
28 |
29 | - Remove story2sketch dependency [#3](https://github.com/intuit/storybook-addon-sketch/pull/3) ([@mcrosby114](https://github.com/mcrosby114))
30 |
31 | #### ⚠️ Pushed to master
32 |
33 | - Update package.json ([@hipstersmoothie](https://github.com/hipstersmoothie))
34 |
35 | #### Authors: 2
36 |
37 | - Matthew Crosby ([@mcrosby114](https://github.com/mcrosby114))
38 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
39 |
40 | ---
41 |
42 | # v0.1.0 (Mon Sep 23 2019)
43 |
44 | #### 🚀 Enhancement
45 |
46 | - add preset [#1](https://github.com/intuit/storybook-addon-sketch/pull/1) ([@hipstersmoothie](https://github.com/hipstersmoothie))
47 |
48 | #### ⚠️ Pushed to master
49 |
50 | - Revert "delete workflows" ([@hipstersmoothie](https://github.com/hipstersmoothie))
51 | - delete workflows ([@hipstersmoothie](https://github.com/hipstersmoothie))
52 | - update url ([@hipstersmoothie](https://github.com/hipstersmoothie))
53 | - :pray: ([@hipstersmoothie](https://github.com/hipstersmoothie))
54 | - de-intuit it ([@hipstersmoothie](https://github.com/hipstersmoothie))
55 |
56 | #### Authors: 1
57 |
58 | - Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
59 |
60 | ---
61 |
62 | # v0.0.2 (Wed Sep 18 2019)
63 |
64 | #### 🐛 Bug Fix
65 |
66 | - make storybook related deps peers [#2](https://github.intuit.com/design-systems/storybook-addon-sketch/pull/2) ([@alisowski](https://github.intuit.com/alisowski))
67 |
68 | #### Authors: 1
69 |
70 | - Andrew Lisowski ([@alisowski](https://github.intuit.com/alisowski))
71 |
72 | ---
73 |
74 | # v0.0.1 (Wed Apr 24 2019)
75 |
76 | #### ⚠️ Pushed to master
77 |
78 | - scope package ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
79 | - update config to point to internal github ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
80 | - update author ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
81 | - add repo ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
82 | - add ci ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
83 | - add docs and add `kind` option ([@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com))
84 |
85 | #### Authors: 1
86 |
87 | - [@lisowski54@gmail.com](https://github.intuit.com/lisowski54@gmail.com)
--------------------------------------------------------------------------------
/src/components/sketch-download.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import join from 'url-join';
3 |
4 | import {
5 | Icons,
6 | IconButton,
7 | WithTooltip,
8 | TooltipLinkList
9 | } from '@storybook/components';
10 | import downloadFile from '../download-file';
11 |
12 | const downloadKind = (api: any) => () => {
13 | const withoutIndex = window.location.pathname
14 | .split('/')
15 | .slice(0, -1)
16 | .join('/');
17 | const baseUrl = join(window.location.origin, withoutIndex);
18 | const kind = api
19 | .getCurrentStoryData()
20 | .kind.replace(/ /g, '_')
21 | .replace(/\//g, '+');
22 | const file = kind + '.asketch.json';
23 | const url = join(baseUrl, 'sketch', file);
24 |
25 | fetch(url)
26 | .then(data => data.json())
27 | .then(json => downloadFile(file, json));
28 | };
29 |
30 | const downloadCurrent = (api: any) => () => {
31 | const iframe = document.querySelector('iframe');
32 |
33 | if (iframe && iframe.contentWindow) {
34 | iframe.contentWindow.saveCurrent(api.getCurrentStoryData().id);
35 | }
36 | };
37 |
38 | const createBackgroundSelectorItem = (id: string, click: () => void) => ({
39 | id: id,
40 | title: id,
41 | onClick: click,
42 | value: id
43 | });
44 |
45 | export interface Options {
46 | kind: boolean;
47 | }
48 |
49 | interface SketchPluginProps {
50 | api: any;
51 | options: Options;
52 | }
53 |
54 | export default class SketchPlugin extends React.Component {
55 | state = {
56 | selected: null,
57 | expanded: false
58 | };
59 |
60 | static defaultProps = {
61 | options: {}
62 | };
63 |
64 | change = (options: { selected: string }) =>
65 | this.setState({ selected: options.selected, expanded: false });
66 |
67 | onVisibilityChange = (expanded: boolean) => {
68 | if (this.state.expanded !== expanded) {
69 | this.setState({ expanded });
70 | }
71 | };
72 |
73 | render() {
74 | const Button: React.FC> = props => (
75 |
76 |
77 |
78 | );
79 |
80 | if (!this.props.options.kind) {
81 | return ;
82 | }
83 |
84 | const links = [
85 | createBackgroundSelectorItem(
86 | 'Download Sketch files',
87 | downloadKind(this.props.api)
88 | ),
89 | createBackgroundSelectorItem(
90 | 'Download Sketch Files for current story configuration',
91 | downloadCurrent(this.props.api)
92 | )
93 | ];
94 |
95 | return (
96 | }
102 | closeOnClick
103 | >
104 |
105 |
106 | );
107 | }
108 | }
109 |
--------------------------------------------------------------------------------