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

feat: Add initial TypeScript autocomplete support #35

Merged
merged 5 commits into from Dec 18, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
@@ -1,7 +1,8 @@
{
"extends": "seek",
"globals": {
"__PLAYROOM_GLOBAL__CONFIG__": true
"__PLAYROOM_GLOBAL__CONFIG__": true,
"__PLAYROOM_GLOBAL__STATIC_TYPES__": true
},
"rules": {
"no-console": 0,
Expand Down
60 changes: 43 additions & 17 deletions README.md
Expand Up @@ -23,7 +23,7 @@ Playroom allows you to create a zero-install code-oriented design environment, b

Send us a PR if you'd like to be in this list!

## Setup
## Getting Started

```bash
$ npm install --save-dev playroom
Expand Down Expand Up @@ -63,7 +63,7 @@ module.exports = {
};
```

Your `components` and `themes` files are expected to export a single object or a series of named exports. For example, your components file might look like this:
Your `components` file is expected to export a single object or a series of named exports. For example:

```js
module.exports = {
Expand All @@ -73,17 +73,21 @@ module.exports = {
};
```

When providing themes, your themes file might look something like this:
Now that your project is configured, you can start a local development server:

```js
module.exports = {
themeA: require('./themeA'),
themeB: require('./themeB')
// etc...
};
```bash
$ npm run playroom:start
```

To build your assets for production:

```bash
$ npm run playroom:build
```

If your components need to be nested within custom provider components, you can provide a custom React component file via the `frameComponent` option, which is a path to a file that might look something like this:
## Custom Frame Component

If your components need to be nested within custom provider components, you can provide a custom React component file via the `frameComponent` option, which is a path to a file that exports a component. For example, if your component library has multiple themes:

```js
import React from 'react';
Expand All @@ -94,6 +98,22 @@ export default ({ theme, children }) => (
);
```

## Theme Support

If your component library has multiple themes, you can customise Playroom to render every theme simultaneously via the `themes` configuration option.

Similar to your `components` file, your `themes` file is expected to export a single object or a series of named exports. For example:

```js
module.exports = {
themeA: require('./themeA'),
themeB: require('./themeB')
// etc...
};
```

## CSS-in-JS Support

If you're using a CSS-in-JS library that generates styles dynamically, you might need to configure it to insert them into the iframe. For example, when using [styled-components](https://www.styled-components.com):

```js
Expand All @@ -108,16 +128,22 @@ export default ({ theme, children, frameWindow }) => (
);
```

Now that your project is configured, you can start a local development server:
## TypeScript Support

```bash
$ npm run playroom:start
```
If a `tsconfig.json` file is present in your project, static prop types are parsed using [react-docgen-typescript](https://github.com/styleguidist/react-docgen-typescript) to provide better autocompletion in the Playroom editor.

To build your assets for production:
**By default, all `.ts` and `.tsx` files in the current working directory are included, excluding `node_modules`.**

```bash
$ npm run playroom:build
If you need to customise this behaviour, you can provide a `typeScriptFiles` option in `playroom.config.js`, which is an array of globs.

```js
module.exports = {
...,
typeScriptFiles: [
'src/components/**/*.{ts,tsx}',
'!**/node_modules'
]
};
```

## License
Expand Down
12 changes: 11 additions & 1 deletion cypress/integration/smokeTest.spec.js
@@ -1,4 +1,8 @@
import { typeCode, assertFrameContains } from '../support/utils';
import {
typeCode,
assertFrameContains,
assertTextareaContains
} from '../support/utils';

describe('Smoke test', () => {
it('works', () => {
Expand All @@ -8,4 +12,10 @@ describe('Smoke test', () => {
typeCode('<Bar />');
assertFrameContains('Bar');
});

it('autocompletes', () => {
typeCode('<F{enter} c{enter}={downarrow}{enter} />');
assertFrameContains('Foo');
assertTextareaContains('<Foo color="blue" />');
});
});
18 changes: 16 additions & 2 deletions cypress/projects/basic/components.js
@@ -1,4 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';

export const Foo = () => <div>Foo</div>;
export const Bar = () => <div>Bar</div>;
const withPropTypes = component => {
component.propTypes = {
color: PropTypes.oneOf(['red', 'blue'])
};

return component;
};

export const Foo = withPropTypes(({ color }) => (
<div style={{ color }}>Foo</div>
));

export const Bar = withPropTypes(({ color }) => (
<div style={{ color }}>Bar</div>
));
9 changes: 8 additions & 1 deletion cypress/support/utils.js
Expand Up @@ -4,7 +4,7 @@ export const typeCode = code =>
.click()
.focused()
.clear({ force: true })
.type(code, { force: true });
.type(code, { force: true, delay: 100 });

export const assertFrameContains = async text => {
const iframe = await cy.get('iframe').first();
Expand All @@ -14,3 +14,10 @@ export const assertFrameContains = async text => {
.find('body')
.contains(text);
};

export const assertTextareaContains = async text => {
return cy
.get('textarea')
.first()
.contains(text);
};
13 changes: 13 additions & 0 deletions examples/typescript/components/Bar/Bar.tsx
@@ -0,0 +1,13 @@
import React, { Component } from 'react';

interface Props {
color: 'red' | 'blue';
}

export default class Bar extends Component<Props> {
render() {
const { color } = this.props;

return <div style={{ color }}>Bar</div>;
}
}
13 changes: 13 additions & 0 deletions examples/typescript/components/Foo/Foo.tsx
@@ -0,0 +1,13 @@
import React, { Component } from 'react';

interface Props {
color: 'red' | 'blue';
}

export default class Foo extends Component<Props> {
render() {
const { color } = this.props;

return <div style={{ color }}>Foo</div>;
}
}
2 changes: 2 additions & 0 deletions examples/typescript/components/index.ts
@@ -0,0 +1,2 @@
export { default as Foo } from './Foo/Foo';
export { default as Bar } from './Bar/Bar';
22 changes: 22 additions & 0 deletions examples/typescript/package.json
@@ -0,0 +1,22 @@
{
"name": "typescript-playroom-example",
"private": true,
"version": "0.0.0",
"description": "TypeScript example for Braid Design System",
"scripts": {
"build": "node ./../../bin/cli build",
"start": "node ./../../bin/cli start"
},
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.2.0",
"@babel/preset-env": "^7.2.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"@types/react": "^16.7.13",
"babel-loader": "^8.0.4"
},
"dependencies": {
"react": "^16.6.3"
}
}
27 changes: 27 additions & 0 deletions examples/typescript/playroom.config.js
@@ -0,0 +1,27 @@
module.exports = {
components: './components/index.ts',
outputPath: './dist',
webpackConfig: () => ({
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
'@babel/preset-react'
]
}
}
}
]
},
resolve: {
extensions: ['.js', '.ts', '.tsx']
}
})
};
14 changes: 14 additions & 0 deletions examples/typescript/tsconfig.json
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"jsx": "preserve"
},
"include": [
"components/**/*"
],
"exclude": [
"node_modules"
]
}