Skip to content

Commit c423982

Browse files
committedNov 7, 2019
Initial commit 🍕
0 parents  commit c423982

14 files changed

+9111
-0
lines changed
 

‎.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
*.log
2+
.DS_Store
3+
node_modules
4+
.cache
5+
.rts2_cache_cjs
6+
.rts2_cache_esm
7+
.rts2_cache_umd
8+
.rts2_cache_system
9+
dist

‎.vscode/settings.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"workbench.colorCustomizations": {
3+
"statusBar.background": "#0538d7",
4+
"statusBarItem.hoverBackground": "#154dfa",
5+
"statusBar.foreground": "#e7e7e7"
6+
},
7+
"peacock.color": "#0538d7"
8+
}

‎LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Benjamin Johnson
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.md

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# TSDX React User Guide
2+
3+
Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it.
4+
5+
> This TSDX setup is meant for developing React components (not apps!) that can be published to NPM. If you’re looking to build an app, you should use `create-react-app`, `razzle`, `nextjs`, `gatsby`, or `react-static`.
6+
7+
> If you’re new to TypeScript and React, checkout [this handy cheatsheet](https://github.com/sw-yx/react-typescript-cheatsheet/)
8+
9+
## Commands
10+
11+
TSDX scaffolds your new library inside `/src`, and also sets up a [Parcel-based](https://parceljs.org) playground for it inside `/example`.
12+
13+
The recommended workflow is to run TSDX in one terminal:
14+
15+
```
16+
npm start # or yarn start
17+
```
18+
19+
This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`.
20+
21+
Then run the example inside another:
22+
23+
```
24+
cd example
25+
npm i # or yarn to install dependencies
26+
npm start # or yarn start
27+
```
28+
29+
The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**, [we use Parcel's aliasing](https://github.com/palmerhq/tsdx/pull/88/files).
30+
31+
To do a one-off build, use `npm run build` or `yarn build`.
32+
33+
To run tests, use `npm test` or `yarn test`.
34+
35+
## Configuration
36+
37+
Code quality is [set up for you](https://github.com/palmerhq/tsdx/pull/45/files) with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly.
38+
39+
### Jest
40+
41+
Jest tests are set up to run with `npm test` or `yarn test`. This runs the test watcher (Jest) in an interactive mode. By default, runs tests related to files changed since the last commit.
42+
43+
#### Setup Files
44+
45+
This is the folder structure we set up for you:
46+
47+
```
48+
/example
49+
index.html
50+
index.tsx # test your component here in a demo app
51+
package.json
52+
tsconfig.json
53+
/src
54+
index.tsx # EDIT THIS
55+
/test
56+
blah.test.tsx # EDIT THIS
57+
.gitignore
58+
package.json
59+
README.md # EDIT THIS
60+
tsconfig.json
61+
```
62+
63+
#### React Testing Library
64+
65+
We do not set up `react-testing-library` for you yet, we welcome contributions and documentation on this.
66+
67+
### Rollup
68+
69+
TSDX uses [Rollup v1.x](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details.
70+
71+
### TypeScript
72+
73+
`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs.
74+
75+
## Continuous Integration
76+
77+
### Travis
78+
79+
_to be completed_
80+
81+
### Circle
82+
83+
_to be completed_
84+
85+
## Optimizations
86+
87+
Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:
88+
89+
```js
90+
// ./types/index.d.ts
91+
declare var __DEV__: boolean;
92+
93+
// inside your code...
94+
if (__DEV__) {
95+
console.log('foo');
96+
}
97+
```
98+
99+
You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.
100+
101+
## Module Formats
102+
103+
CJS, ESModules, and UMD module formats are supported.
104+
105+
The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found.
106+
107+
## Using the Playground
108+
109+
```
110+
cd example
111+
npm i # or yarn to install dependencies
112+
npm start # or yarn start
113+
```
114+
115+
The default example imports and live reloads whatever is in `/dist`, so if you are seeing an out of date component, make sure TSDX is running in watch mode like we recommend above. **No symlinking required**!
116+
117+
## Deploying the Playground
118+
119+
The Playground is just a simple [Parcel](https://parceljs.org) app, you can deploy it anywhere you would normally deploy that. Here are some guidelines for **manually** deploying with the Netlify CLI (`npm i -g netlify-cli`):
120+
121+
```bash
122+
cd example # if not already in the example folder
123+
npm run build # builds to dist
124+
netlify deploy # deploy the dist folder
125+
```
126+
127+
Alternatively, if you already have a git repo connected, you can set up continuous deployment with Netlify:
128+
129+
```bash
130+
netlify init
131+
# build command: yarn build && cd example && yarn && yarn build
132+
# directory to deploy: example/dist
133+
# pick yes for netlify.toml
134+
```
135+
136+
## Named Exports
137+
138+
Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library.
139+
140+
## Including Styles
141+
142+
There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like.
143+
144+
For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader.
145+
146+
## Publishing to NPM
147+
148+
We recommend using https://github.com/sindresorhus/np.
149+
150+
## Usage with Lerna
151+
152+
When creating a new package with TSDX within a project set up with Lerna, you might encounter a `Cannot resolve dependency` error when trying to run the `example` project. To fix that you will need to make changes to the `package.json` file _inside the `example` directory_.
153+
154+
The problem is that due to the nature of how dependencies are installed in Lerna projects, the aliases in the example project's `package.json` might not point to the right place, as those dependencies might have been installed in the root of your Lerna project.
155+
156+
Change the `alias` to point to where those packages are actually installed. This depends on the directory structure of your Lerna project, so the actual path might be different from the diff below.
157+
158+
```diff
159+
"alias": {
160+
- "react": "../node_modules/react",
161+
- "react-dom": "../node_modules/react-dom"
162+
+ "react": "../../../node_modules/react",
163+
+ "react-dom": "../../../node_modules/react-dom"
164+
},
165+
```
166+
167+
An alternative to fixing this problem would be to remove aliases altogether and define the dependencies referenced as aliases as dev dependencies instead. [However, that might cause other problems.](https://github.com/palmerhq/tsdx/issues/64)

‎example/.npmignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
.cache
3+
dist

‎example/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
7+
<title>Playground</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
<script src="./index.tsx"></script>
13+
</body>
14+
</html>

‎example/index.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'react-app-polyfill/ie11';
2+
import * as React from 'react';
3+
import * as ReactDOM from 'react-dom';
4+
import { Thing } from '../.';
5+
6+
const App = () => {
7+
return (
8+
<div>
9+
<Thing />
10+
</div>
11+
);
12+
};
13+
14+
ReactDOM.render(<App />, document.getElementById('root'));

‎example/package.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "example",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"scripts": {
7+
"start": "parcel index.html",
8+
"build": "parcel build index.html"
9+
},
10+
"dependencies": {
11+
"react-app-polyfill": "^1.0.0"
12+
},
13+
"alias": {
14+
"react": "../node_modules/react",
15+
"react-dom": "../node_modules/react-dom/profiling",
16+
"scheduler/tracing": "../node_modules/scheduler/tracing-profiling"
17+
},
18+
"devDependencies": {
19+
"@types/react": "^16.9.11",
20+
"@types/react-dom": "^16.8.4",
21+
"parcel": "^1.12.3",
22+
"typescript": "^3.4.5"
23+
}
24+
}

‎example/tsconfig.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"allowSyntheticDefaultImports": false,
4+
"target": "es5",
5+
"module": "commonjs",
6+
"jsx": "react",
7+
"moduleResolution": "node",
8+
"noImplicitAny": false,
9+
"noUnusedLocals": false,
10+
"noUnusedParameters": false,
11+
"removeComments": true,
12+
"strictNullChecks": true,
13+
"preserveConstEnums": true,
14+
"sourceMap": true,
15+
"lib": ["es2015", "es2016", "dom"],
16+
"baseUrl": ".",
17+
"types": ["node"]
18+
}
19+
}

‎package.json

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "react-heading",
3+
"version": "0.1.0",
4+
"license": "MIT",
5+
"author": "Benjamin Johnson",
6+
"main": "dist/index.js",
7+
"module": "dist/react-heading.esm.js",
8+
"typings": "dist/index.d.ts",
9+
"files": [
10+
"dist"
11+
],
12+
"scripts": {
13+
"start": "tsdx watch",
14+
"build": "tsdx build",
15+
"test": "tsdx test --env=jsdom",
16+
"lint": "tsdx lint"
17+
},
18+
"peerDependencies": {
19+
"react": ">=16"
20+
},
21+
"husky": {
22+
"hooks": {
23+
"pre-commit": "tsdx lint"
24+
}
25+
},
26+
"prettier": {
27+
"printWidth": 80,
28+
"semi": false,
29+
"singleQuote": true,
30+
"trailingComma": "es5"
31+
},
32+
"jest": {
33+
"globals": {
34+
"ts-jest": {
35+
"diagnostics": false
36+
}
37+
}
38+
},
39+
"size-limit": [
40+
{
41+
"path": "dist/index.js",
42+
"limit": "500 B"
43+
}
44+
],
45+
"devDependencies": {
46+
"@size-limit/preset-small-lib": "^2.1.6",
47+
"@testing-library/react": "^9.3.2",
48+
"@types/jest": "^24.0.22",
49+
"@types/react": "^16.9.11",
50+
"@types/react-dom": "^16.9.4",
51+
"husky": "^3.0.9",
52+
"react": "^16.11.0",
53+
"react-dom": "^16.11.0",
54+
"tsdx": "^0.11.0",
55+
"tslib": "^1.10.0",
56+
"typescript": "^3.7.2"
57+
}
58+
}

‎src/index.test.tsx

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as React from 'react'
2+
import { render } from '@testing-library/react'
3+
import { H, HLevel } from './index'
4+
5+
describe('headings', () => {
6+
test('renders a h2 if using <H> outside of <HLevel>', () => {
7+
let { container } = render(<H>content</H>)
8+
let $h2 = container.querySelector('h2') as HTMLElement
9+
expect($h2.outerHTML).toEqual('<h2>content</h2>')
10+
})
11+
12+
test('renders dynamic heading levels', () => {
13+
let { container, debug } = render(
14+
<div>
15+
<H>should be an h2</H>
16+
<HLevel>
17+
<H>should be an h3</H>
18+
19+
<HLevel>
20+
<H>should be an h4</H>
21+
</HLevel>
22+
</HLevel>
23+
</div>
24+
)
25+
26+
let tags = ['h2', 'h3', 'h4']
27+
28+
for (let tag of tags) {
29+
let $h = container.querySelector(tag) as HTMLElement
30+
31+
expect($h.outerHTML).toEqual(`<${tag}>should be an ${tag}</${tag}>`)
32+
}
33+
})
34+
35+
test("doesn't render past an h6", () => {
36+
let { container, getByText } = render(
37+
<div>
38+
<HLevel>
39+
<HLevel>
40+
<HLevel>
41+
<HLevel>
42+
<HLevel>
43+
<HLevel>
44+
<HLevel>
45+
<HLevel>
46+
<H>this is a heading</H>
47+
</HLevel>
48+
</HLevel>
49+
</HLevel>
50+
</HLevel>
51+
</HLevel>
52+
</HLevel>
53+
</HLevel>
54+
</HLevel>
55+
</div>
56+
)
57+
58+
let $h = getByText('this is a heading')
59+
expect($h.outerHTML).toEqual('<h6>this is a heading</h6>')
60+
})
61+
})

‎src/index.tsx

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, {
2+
createContext,
3+
HTMLProps,
4+
ReactNode,
5+
createElement,
6+
useContext,
7+
} from 'react'
8+
9+
let DEFAULT_HEADING_LEVEL = 2
10+
11+
export let LevelContext = createContext(DEFAULT_HEADING_LEVEL)
12+
13+
type HProps = HTMLProps<HTMLHeadingElement>
14+
15+
export let H = ({ children, ...rest }: HProps) => {
16+
let level = useContext(LevelContext)
17+
18+
let tag = 'h' + Math.min(level, 6)
19+
20+
return createElement(tag, rest, children)
21+
}
22+
23+
type HLevelProps = { children: ReactNode }
24+
25+
export let HLevel = ({ children }: HLevelProps) => {
26+
let level = useContext(LevelContext)
27+
28+
return (
29+
<LevelContext.Provider value={level + 1}>{children}</LevelContext.Provider>
30+
)
31+
}

‎tsconfig.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"include": ["src", "types", "test"],
3+
"compilerOptions": {
4+
"target": "es5",
5+
"module": "esnext",
6+
"lib": ["dom", "esnext"],
7+
"importHelpers": true,
8+
"declaration": true,
9+
"sourceMap": true,
10+
"rootDir": "./",
11+
"strict": true,
12+
"noImplicitAny": true,
13+
"strictNullChecks": true,
14+
"strictFunctionTypes": true,
15+
"strictPropertyInitialization": true,
16+
"noImplicitThis": true,
17+
"alwaysStrict": true,
18+
"noUnusedLocals": true,
19+
"noUnusedParameters": true,
20+
"noImplicitReturns": true,
21+
"noFallthroughCasesInSwitch": true,
22+
"moduleResolution": "node",
23+
"baseUrl": "./",
24+
"paths": {
25+
"*": ["src/*", "node_modules/*"]
26+
},
27+
"jsx": "react",
28+
"esModuleInterop": true
29+
}
30+
}

‎yarn.lock

+8,652
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.