Skip to content

Commit

Permalink
Add CRA/TypeScript example project (mui#8694)
Browse files Browse the repository at this point in the history
  • Loading branch information
pelotom authored and the-noob committed Oct 17, 2017
1 parent 42e2327 commit b3a9c15
Show file tree
Hide file tree
Showing 22 changed files with 583 additions and 13 deletions.
3 changes: 3 additions & 0 deletions docs/src/modules/components/withRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ const pages = [
{
pathname: '/guides/flow',
},
{
pathname: '/guides/typescript',
},
],
},
{
Expand Down
1 change: 1 addition & 0 deletions docs/src/pages/getting-started/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ We host some example projects. You can find them in our [GitHub repository](http
- [Create React App](https://github.com/callemall/material-ui/tree/v1-beta/examples/create-react-app)
- [Next.js](https://github.com/callemall/material-ui/tree/v1-beta/examples/nextjs)
- [Create React App with Flow](https://github.com/callemall/material-ui/tree/v1-beta/examples/create-react-app-with-flow)
- [Create React App with TypeScript](https://github.com/callemall/material-ui/tree/v1-beta/examples/create-react-app-with-typescript)

The source code for this documentation site is also included in the repository.
This is a slightly more complex project.
Expand Down
14 changes: 1 addition & 13 deletions docs/src/pages/guides/flow.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
# Flow

You can add static typing to JavaScript to improve developer productivity and code quality thanks to [Flow](https://github.com/facebook/flow)

## Installation in your own project

You can add static typing to JavaScript to improve developer productivity and code quality thanks to [Flow](https://github.com/facebook/flow).
Have a look at the [Create React App with Flow](https://github.com/callemall/material-ui/tree/v1-beta/examples/create-react-app-with-flow) example.

1. Copy `.flowconfig`
1. Change `module.name_mapper` to your project name
1. Copy `flow` dir to seed your own local libdefs (in case you need any)
1. `yarn add -D flow-bin`
1. Decide on `enzyme` - flow is expecting it in your `package.json` because `material-ui` includes reusable `test-utils`. If you do not want it, uncomment `L12` in the `.flowconfig`
1. Copy `package.json` script `flow`
1. `flow-typed install`
1. `yarn flow`
89 changes: 89 additions & 0 deletions docs/src/pages/guides/typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# TypeScript

You can add static typing to JavaScript to improve developer productivity and code quality thanks to [TypeScript](https://www.typescriptlang.org/).
Have a look at the [Create React App with TypeScript](https://github.com/callemall/material-ui/tree/v1-beta/examples/create-react-app-with-typescript) example.

## Usage of `withStyles`

The usage of `withStyles` in TypeScript can be a little tricky, so it's worth showing some examples. You can first call `withStyles()` to create a decorator function, like so:

```jsx
const decorate = withStyles(({ palette, spacing }) => ({
root: {
padding: spacing.unit,
background: palette.background,
color: palette.primary,
},
}));
```

This can then subsequently be used to decorate either a stateless functional component or a class component. Suppose we have in either case the following props:

```jsx
interface Props {
text: string;
type: TypographyProps['type'];
color: TypographyProps['color'];
}
```

Functional components are straightforward:

```jsx
const DecoratedSFC = decorate<Props>(({ text, type, color, classes }) => (
<Typography type={type} color={color} classes={classes}>
{text}
</Typography>
));
```

Class components are a little more cumbersome. Due to a [current limitation in TypeScript's decorator support](https://github.com/Microsoft/TypeScript/issues/4881), `withStyles` can't be used as a class decorator. Instead, we decorate a class component like so:

```jsx
const DecoratedClass = decorate(
class extends React.Component<Props & WithStyles<'root'>> {
render() {
const { text, type, color, classes } = this.props
return (
<Typography type={type} color={color} classes={classes}>
{text}
</Typography>
)
}
}
);
```

Note that in the class example you didn't need to annotate `<Props>` in the call to `decorate`; type inference took care of everything. One caveat is that if your styled component takes _no_ additional props in addition to `classes`. The natural thing would be to write

```jsx
const DecoratedNoProps = decorate(
class extends React.Component<WithStyles<'root'>> {
render() {
return (
<Typography classes={this.props.classes}>
Hello, World!
</Typography>
)
}
}
);
```

Unfortunately, TypeScript infers the wrong type in this case and you'll have trouble when you go to make an element of this component. In this case, you'll need to provide an explicit `{}` type argument, like so:

```jsx
const DecoratedNoProps = decorate<{}>( // <-- note the type argument!
class extends React.Component<WithStyles<'root'>> {
render() {
return (
<Typography classes={this.props.classes}>
Hello, World!
</Typography>
)
}
}
);
```

To avoid worrying about this edge case it may be a good habit to always provide an explicit type argument to `decorate`.
3 changes: 3 additions & 0 deletions examples/create-react-app-with-flow/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
<title>My page</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
Expand Down
21 changes: 21 additions & 0 deletions examples/create-react-app-with-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
/node_modules

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
20 changes: 20 additions & 0 deletions examples/create-react-app-with-typescript/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Create React App example with TypeScript

## How to use

Download the example [or clone the repo](https://github.com/callemall/material-ui):

```bash
curl https://codeload.github.com/callemall/material-ui/tar.gz/v1-beta | tar -xz --strip=2 material-ui-1-beta/examples/create-react-app-with-typescript
cd create-react-app-with-typescript
```

Install it and run:

```bash
yarn start
```

## The idea behind the example

This example demonstrate how you can use [Create React App](https://github.com/facebookincubator/create-react-app) with [TypeScript](https://www.typescriptlang.org/).
24 changes: 24 additions & 0 deletions examples/create-react-app-with-typescript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "create-react-app-with-typescript",
"version": "0.1.0",
"private": true,
"dependencies": {
"material-ui": "next",
"react": "latest",
"react-dom": "latest",
"react-scripts-ts": "latest"
},
"devDependencies": {
"@types/jest": "latest",
"@types/node": "latest",
"@types/react": "latest",
"@types/react-dom": "latest",
"@types/recompose": "latest"
},
"scripts": {
"start": "react-scripts-ts start",
"build": "react-scripts-ts build",
"test": "react-scripts-ts test --env=jsdom",
"eject": "react-scripts-ts eject"
}
}
Binary file not shown.
42 changes: 42 additions & 0 deletions examples/create-react-app-with-typescript/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=0, initial-scale=1, minimum-scale=1, width=device-width, height=device-height">
<!-- PWA primary color -->
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" />
<title>My page</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
15 changes: 15 additions & 0 deletions examples/create-react-app-with-typescript/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as React from 'react';
import JssProvider from 'react-jss/lib/JssProvider';
import { withStyles, MuiThemeProvider } from 'material-ui/styles';
import { wrapDisplayName } from 'recompose';
import createContext from '../styles/createContext';

// Apply some reset
const decorate = withStyles(theme => ({
'@global': {
html: {
background: theme.palette.background.default,
WebkitFontSmoothing: 'antialiased', // Antialiasing.
MozOsxFontSmoothing: 'grayscale', // Antialiasing.
},
body: {
margin: 0,
},
},
}));

const AppWrapper = decorate<{ children: JSX.Element }>(props => props.children);

const context = createContext();

function withRoot(BaseComponent: React.ComponentType) {
class WithRoot extends React.Component {
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}

render() {
return (
<JssProvider registry={context.sheetsRegistry} jss={context.jss}>
<MuiThemeProvider theme={context.theme} sheetsManager={context.sheetsManager}>
<AppWrapper>
<BaseComponent />
</AppWrapper>
</MuiThemeProvider>
</JssProvider>
);
}
}

if (process.env.NODE_ENV !== 'production') {
(WithRoot as any).displayName = wrapDisplayName(BaseComponent, 'withRoot');
}

return WithRoot;
}

export default withRoot;
5 changes: 5 additions & 0 deletions examples/create-react-app-with-typescript/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from 'react';
import { render } from 'react-dom';
import Index from './pages/index';

render(<Index />, document.querySelector('#root'));
69 changes: 69 additions & 0 deletions examples/create-react-app-with-typescript/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react';
import Button from 'material-ui/Button';
import Dialog, {
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
} from 'material-ui/Dialog';
import Typography from 'material-ui/Typography';
import withStyles, { WithStyles } from 'material-ui/styles/withStyles';
import withRoot from '../components/withRoot';

const styles = {
root: {
textAlign: 'center',
paddingTop: 200,
},
};

type State = {
open: boolean,
};

class Index extends React.Component<WithStyles<keyof typeof styles>, State> {
state = {
open: false,
};

handleRequestClose = () => {
this.setState({
open: false,
});
};

handleClick = () => {
this.setState({
open: true,
});
};

render() {
return (
<div className={this.props.classes.root}>
<Dialog open={this.state.open} onRequestClose={this.handleRequestClose}>
<DialogTitle>Super Secret Password</DialogTitle>
<DialogContent>
<DialogContentText>1-2-3-4-5</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={this.handleRequestClose}>
OK
</Button>
</DialogActions>
</Dialog>
<Typography type="display1" gutterBottom>
Material-UI
</Typography>
<Typography type="subheading" gutterBottom>
example project
</Typography>
<Button raised color="accent" onClick={this.handleClick}>
Super Secret Password
</Button>
</div>
);
}
}

export default withRoot(withStyles(styles)<{}>(Index));
Loading

0 comments on commit b3a9c15

Please sign in to comment.