├── public
├── _redirects
├── favicon.ico
├── manifest.json
└── index.html
├── .eslintignore
├── .npmrc
├── .prettierignore
├── src
├── setupTest.js
├── docs
│ ├── docs.mdx
│ ├── docs.js
│ └── mock-query-response.json
├── screens
│ ├── user
│ │ ├── user-context.js
│ │ ├── components
│ │ │ ├── repo-filter.js
│ │ │ ├── __tests__
│ │ │ │ ├── repo-filter.js
│ │ │ │ └── query.js
│ │ │ ├── query.js
│ │ │ ├── repo-list.js
│ │ │ └── profile.js
│ │ └── index.js
│ └── home
│ │ ├── docs.mdx
│ │ └── index.js
├── shared
│ ├── theme-provider.js
│ ├── loading.js
│ ├── layout.js
│ └── pattern.js
├── index.js
├── global-styles.css
└── github-client.js
├── .env
├── .travis.yml
├── .prettierrc
├── lint-staged.config.js
├── README.md
├── .gitignore
├── LICENSE
└── package.json
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage/
2 | build/
3 | node_modules/
4 | .docz/
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=http://registry.npmjs.org/
2 | package-lock=true
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .git
2 | package-lock.json
3 | node_modules
4 | .docz
5 | build
6 | coverage
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kentcdodds/react-github-profile/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/setupTest.js:
--------------------------------------------------------------------------------
1 | import 'jest-dom/extend-expect'
2 | import 'react-testing-library/cleanup-after-each'
3 |
--------------------------------------------------------------------------------
/src/docs/docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: App
3 | route: /
4 | ---
5 |
6 | import DocsApp from './docs'
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/screens/user/user-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const UserContext = React.createContext()
4 |
5 | export default UserContext
6 |
--------------------------------------------------------------------------------
/src/screens/home/docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Home
3 | menu: Components
4 | route: /components/home
5 | ---
6 |
7 | import Home from '.'
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # sadly docz wants to install a different version of webpack than react-scripts
2 | SKIP_PREFLIGHT_CHECK=true
3 | REACT_APP_NETLIFY_SITE_ID=2b9c1652-1f15-4c58-89f2-290796d9fc68
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js: 8
4 | script: npm run validate
5 | notifications:
6 | email: false
7 | branches:
8 | only:
9 | - master
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": false,
6 | "singleQuote": true,
7 | "trailingComma": "all",
8 | "bracketSpacing": false,
9 | "jsxBracketSameLine": false
10 | }
11 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | linters: {
3 | '**/*.+(js|json|less|css|ts|tsx|md)': [
4 | 'prettier',
5 | 'npm run test -- --coverage --findRelatedTests',
6 | 'git add',
7 | ],
8 | },
9 | }
10 |
--------------------------------------------------------------------------------
/src/shared/theme-provider.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {ThemeProvider} from 'emotion-theming'
3 |
4 | const theme = {
5 | common: {
6 | borderBottom: {borderBottom: '1px solid #eee'},
7 | },
8 | colors: {
9 | faded: '#666',
10 | fadedExtra: '#888',
11 | },
12 | }
13 |
14 | export default props =>
15 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-github-profile
2 |
3 | An app I'm going to use to teach people about React 👍
4 |
5 | ## Setup
6 |
7 | TODO
8 |
9 | ## Notes
10 |
11 | ### Folder structure
12 |
13 | The folder structure comes from [this gist](https://gist.github.com/ryanflorence/daafb1e3cb8ad740b346)
14 |
15 | I'm a big fan of it. That's why we're using it :)
16 |
17 | ## LICENSE
18 |
19 | MIT
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | .docz
24 |
--------------------------------------------------------------------------------
/src/shared/loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const sizes = {
4 | small: {zoom: 0.7},
5 | medium: {zoom: 1},
6 | large: {zoom: 2},
7 | }
8 |
9 | function Loading({size = 'medium', className = '', ...props}) {
10 | return (
11 |
17 | )
18 | }
19 |
20 | export default Loading
21 |
--------------------------------------------------------------------------------
/src/screens/user/components/repo-filter.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import {Section, Input} from '../../../shared/pattern'
4 |
5 | export default RepoFilter
6 |
7 | function RepoFilter({filter, onUpdate}) {
8 | return (
9 |
18 | )
19 | }
20 |
21 | RepoFilter.propTypes = {
22 | filter: PropTypes.string.isRequired,
23 | onUpdate: PropTypes.func.isRequired,
24 | }
25 |
--------------------------------------------------------------------------------
/src/screens/user/components/__tests__/repo-filter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {render, fireEvent} from 'react-testing-library'
3 | import ThemeProvider from '../../../../shared/theme-provider'
4 | import RepoFilter from '../repo-filter'
5 |
6 | test('when the user changes the input, the onUpdate prop is called with the new text', () => {
7 | const onUpdate = jest.fn()
8 | const {getByLabelText} = render(
9 |
10 |
11 | ,
12 | )
13 | const value = 'a'
14 | fireEvent.change(getByLabelText(/filter/i), {target: {value}})
15 | expect(onUpdate).toHaveBeenCalledTimes(1)
16 | expect(onUpdate).toHaveBeenCalledWith(value)
17 | })
18 |
--------------------------------------------------------------------------------
/src/docs/docs.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | import 'style-loader!css-loader!../global-styles.css'
3 | import React from 'react'
4 | import ThemeProvider from '../shared/theme-provider'
5 | import User from '../screens/user'
6 | import * as GitHubContext from '../github-client'
7 | import data from './mock-query-response.json'
8 |
9 | const fakeClient = {request: () => Promise.resolve(data)}
10 |
11 | function DocsApp() {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default DocsApp
24 | export {fakeClient}
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Matt Zabriskie and Kent C. Dodds
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/src/shared/layout.js:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled/macro'
2 |
3 | const sizes = {
4 | phone: 768,
5 | tablet: 992,
6 | desktop: 1200,
7 | }
8 |
9 | const queries = {
10 | tiny: `@media (max-width: ${sizes.phone}px)`,
11 | phone: `@media (min-width: ${sizes.phone}px)`,
12 | tablet: `@media (min-width: ${sizes.tablet}px)`,
13 | desktop: `@media (min-width: ${sizes.desktop}px)`,
14 | }
15 |
16 | const Container = styled.div({
17 | label: 'container',
18 | paddingRight: '15px',
19 | paddingLeft: '15px',
20 | marginRight: 'auto',
21 | marginLeft: 'auto',
22 | [queries.phone]: {
23 | maxWidth: '750px',
24 | },
25 | [queries.tablet]: {
26 | maxWidth: '970px',
27 | },
28 | [queries.desktop]: {
29 | maxWidth: '1170px',
30 | },
31 | })
32 |
33 | const Row = styled.div({
34 | label: 'row',
35 | marginRight: '-15px',
36 | marginLeft: '-15px',
37 | [queries.phone]: {
38 | display: 'flex',
39 | justifyContent: 'center',
40 | },
41 | })
42 |
43 | const Column = styled.div(
44 | {
45 | label: 'column',
46 | position: 'relative',
47 | minHeight: '1px',
48 | paddingRight: '15px',
49 | paddingLeft: '15px',
50 | },
51 | ({width}) => ({
52 | [queries.phone]: {
53 | flex: width,
54 | },
55 | }),
56 | )
57 |
58 | export {queries, Container, Row, Column}
59 |
--------------------------------------------------------------------------------
/src/screens/home/index.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import {navigate} from '@reach/router'
5 | import {Input, PrimaryButton, IsolatedContainer} from '../../shared/pattern'
6 |
7 | function handleSubmit(e) {
8 | e.preventDefault()
9 | const username = e.target.elements.username.value.trim()
10 | navigate(`/${username}`)
11 | }
12 |
13 | function Home() {
14 | return (
15 |
16 |
47 |
48 | )
49 | }
50 |
51 | export default Home
52 |
53 | /*
54 | eslint
55 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
56 | react/react-in-jsx-scope: "off"
57 | */
58 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './global-styles.css'
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import {Router} from '@reach/router'
5 | import ErrorBoundary from 'react-error-boundary'
6 | import loadable from 'react-loadable'
7 | import ThemeProvider from './shared/theme-provider'
8 | import {IsolatedContainer, LoadingMessagePage} from './shared/pattern'
9 | import * as GitHubContext from './github-client'
10 |
11 | function LoadingFallback({error, pastDelay}) {
12 | if (error) {
13 | // our ErrorBoundary will catch this
14 | throw error
15 | }
16 | return Loading Application
17 | }
18 |
19 | const Home = loadable({
20 | loader: () => import('./screens/home'),
21 | loading: LoadingFallback,
22 | })
23 |
24 | const User = loadable({
25 | loader: () => import('./screens/user'),
26 | loading: LoadingFallback,
27 | })
28 |
29 | function ErrorFallback({error}) {
30 | return (
31 |
32 | There was an error
33 | {JSON.stringify(error, null, 2)}
34 |
35 | )
36 | }
37 |
38 | function App() {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
53 | const ui =
54 | const container = document.getElementById('root')
55 |
56 | ReactDOM.render(ui, container)
57 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-github-profile",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emotion/core": "10.0.2",
7 | "@emotion/styled": "10.0.2",
8 | "@reach/router": "1.2.1",
9 | "date-fns": "1.29.0",
10 | "emotion": "10.0.2",
11 | "emotion-theming": "10.0.2",
12 | "graphql-request": "1.8.2",
13 | "match-sorter": "2.3.0",
14 | "netlify-auth-providers": "1.0.0-alpha5",
15 | "prop-types": "15.6.2",
16 | "react": "16.7.0-alpha.2",
17 | "react-cache": "2.0.0-alpha.1",
18 | "react-dom": "16.7.0-alpha.2",
19 | "react-error-boundary": "1.2.3",
20 | "react-loadable": "^5.5.0"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "npm run build:app && npm run build:docs",
25 | "build:app": "react-scripts build",
26 | "build:docs": "docz build --dest build/docz",
27 | "test": "react-scripts test",
28 | "dev": "docz dev --port 3001",
29 | "format": "prettier --write \"**/*.+(js|json|css)\"",
30 | "validate": "cross-env CI=true npm-run-all --parallel test build"
31 | },
32 | "husky": {
33 | "hooks": {
34 | "pre-commit": "lint-staged && npm run build -s"
35 | }
36 | },
37 | "eslintConfig": {
38 | "extends": "react-app"
39 | },
40 | "browserslist": [
41 | ">0.2%",
42 | "not dead",
43 | "not ie <= 11",
44 | "not op_mini all"
45 | ],
46 | "devDependencies": {
47 | "cross-env": "5.2.0",
48 | "docz": "0.12.9",
49 | "husky": "1.1.3",
50 | "jest-dom": "2.1.1",
51 | "lint-staged": "8.0.4",
52 | "lodash": "4.17.11",
53 | "normalize.css": "8.0.0",
54 | "npm-run-all": "4.1.3",
55 | "prettier": "1.14.3",
56 | "react-scripts": "2.1.1",
57 | "react-testing-library": "5.3.0"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/screens/user/components/query.js:
--------------------------------------------------------------------------------
1 | import {Component} from 'react'
2 | import PropTypes from 'prop-types'
3 | import isEqual from 'lodash/isEqual'
4 | import * as GitHub from '../../../github-client'
5 |
6 | class Query extends Component {
7 | static propTypes = {
8 | query: PropTypes.string.isRequired,
9 | variables: PropTypes.object,
10 | children: PropTypes.func.isRequired,
11 | normalize: PropTypes.func,
12 | }
13 | static defaultProps = {
14 | normalize: data => data,
15 | }
16 | static contextType = GitHub.Context
17 |
18 | state = {loaded: false, fetching: false, data: null, error: null}
19 |
20 | componentDidMount() {
21 | this._isMounted = true
22 | this.query()
23 | }
24 |
25 | componentDidUpdate(prevProps) {
26 | if (
27 | !isEqual(this.props.query, prevProps.query) ||
28 | !isEqual(this.props.variables, prevProps.variables)
29 | ) {
30 | this.query()
31 | }
32 | }
33 |
34 | componentWillUnmount() {
35 | this._isMounted = false
36 | }
37 |
38 | query() {
39 | this.setState({fetching: true})
40 | const client = this.context
41 | client
42 | .request(this.props.query, this.props.variables)
43 | .then(res =>
44 | this.safeSetState({
45 | data: this.props.normalize(res),
46 | error: null,
47 | loaded: true,
48 | fetching: false,
49 | }),
50 | )
51 | .catch(error =>
52 | this.safeSetState({
53 | error,
54 | data: null,
55 | loaded: false,
56 | fetching: false,
57 | }),
58 | )
59 | }
60 |
61 | safeSetState(...args) {
62 | this._isMounted && this.setState(...args)
63 | }
64 |
65 | render() {
66 | return this.props.children(this.state)
67 | }
68 | }
69 |
70 | export default Query
71 |
--------------------------------------------------------------------------------
/src/screens/user/components/repo-list.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import PropTypes from 'prop-types'
5 | import styled from '@emotion/styled/macro'
6 | import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'
7 | import matchSorter from 'match-sorter'
8 | import {Text, Anchor} from '../../../shared/pattern'
9 | import UserContext from '../user-context'
10 |
11 | function RepoList({repos, filter}) {
12 | const matchingRepos = matchSorter(repos, filter, {
13 | keys: [
14 | 'name',
15 | {maxRanking: matchSorter.rankings.SIMPLE_MATCH, key: 'language'},
16 | {maxRanking: matchSorter.rankings.CONTAINS, key: 'description'},
17 | ],
18 | })
19 | return (
20 |
28 | {matchingRepos.map(repo => (
29 |
30 | ))}
31 |
32 | )
33 | }
34 |
35 | RepoList.propTypes = {
36 | filter: PropTypes.string.isRequired,
37 | repos: PropTypes.arrayOf(
38 | PropTypes.shape({
39 | id: PropTypes.string.isRequired,
40 | }),
41 | ).isRequired,
42 | }
43 |
44 | const ListItem = styled.li(
45 | {padding: '25px 0'},
46 | ({theme}) => theme.common.borderBottom,
47 | )
48 |
49 | const Stat = styled(Text)({marginLeft: 10}).withComponent('strong')
50 | Stat.defaultProps = {tint: 'fadedExtra'}
51 |
52 | function RepoListItem({repo}) {
53 | const timeUpdated = distanceInWordsToNow(repo.pushedAt)
54 | return (
55 |
56 |
57 | {repo.language}
58 | ☆ {repo.stargazersCount}
59 | Ⴤ {repo.forksCount}
60 |
61 |
62 |
63 |
64 | {repo.name}
65 |
66 |
67 |
68 |
69 |
70 | {repo.description}
71 |
72 |
73 |
76 |
77 | )
78 | }
79 |
80 | RepoListItem.propTypes = {
81 | repo: PropTypes.shape({
82 | pushedAt: PropTypes.string,
83 | language: PropTypes.string,
84 | stargazersCount: PropTypes.number,
85 | forksCount: PropTypes.number,
86 | url: PropTypes.string,
87 | name: PropTypes.string,
88 | description: PropTypes.string,
89 | }).isRequired,
90 | }
91 |
92 | function RepoListUserConsumer(props) {
93 | return (
94 |
95 | {userData => }
96 |
97 | )
98 | }
99 |
100 | export default RepoListUserConsumer
101 |
102 | /*
103 | eslint
104 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
105 | react/react-in-jsx-scope: "off"
106 | */
107 |
--------------------------------------------------------------------------------
/src/global-styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | -webkit-box-sizing: border-box;
3 | box-sizing: border-box;
4 | }
5 | *:before,
6 | *:after {
7 | -webkit-box-sizing: border-box;
8 | box-sizing: border-box;
9 | }
10 | html {
11 | font-size: 10px;
12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
13 | }
14 | body {
15 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
16 | font-size: 14px;
17 | line-height: 1.42857143;
18 | color: #333;
19 | background-color: #fff;
20 | margin: 0;
21 | }
22 |
23 | /* thanks to https://loading.io/css/ */
24 | .lds-ellipsis {
25 | display: inline-block;
26 | position: relative;
27 | width: 64px;
28 | height: 64px;
29 | }
30 | .lds-ellipsis div {
31 | position: absolute;
32 | top: 27px;
33 | width: 11px;
34 | height: 11px;
35 | border-radius: 50%;
36 | background: #333;
37 | animation-timing-function: cubic-bezier(0, 1, 1, 0);
38 | }
39 | .lds-ellipsis div:nth-child(1) {
40 | left: 6px;
41 | animation: lds-ellipsis1 0.6s infinite;
42 | }
43 | .lds-ellipsis div:nth-child(2) {
44 | left: 6px;
45 | animation: lds-ellipsis2 0.6s infinite;
46 | }
47 | .lds-ellipsis div:nth-child(3) {
48 | left: 26px;
49 | animation: lds-ellipsis2 0.6s infinite;
50 | }
51 | .lds-ellipsis div:nth-child(4) {
52 | left: 45px;
53 | animation: lds-ellipsis3 0.6s infinite;
54 | }
55 | @keyframes lds-ellipsis1 {
56 | 0% {
57 | transform: scale(0);
58 | }
59 | 100% {
60 | transform: scale(1);
61 | }
62 | }
63 | @keyframes lds-ellipsis3 {
64 | 0% {
65 | transform: scale(1);
66 | }
67 | 100% {
68 | transform: scale(0);
69 | }
70 | }
71 | @keyframes lds-ellipsis2 {
72 | 0% {
73 | transform: translate(0, 0);
74 | }
75 | 100% {
76 | transform: translate(19px, 0);
77 | }
78 | }
79 |
80 | /* Thanks to https://medium.freecodecamp.org/a-step-by-step-guide-to-making-pure-css-tooltips-3d5a3e237346 */
81 | [data-tooltip] {
82 | position: relative;
83 | display: inline-block;
84 | }
85 |
86 | [data-tooltip]::before {
87 | content: '';
88 | position: absolute;
89 | top: -6px;
90 | left: 50%;
91 | transform: translateX(-50%);
92 | border-width: 4px 6px 0 6px;
93 | border-style: solid;
94 | border-color: rgba(0, 0, 0, 0.7) transparent transparent transparent;
95 | z-index: 99;
96 | opacity: 0;
97 | transition: 0.15s opacity;
98 | }
99 |
100 | [data-tooltip]::after {
101 | content: attr(data-tooltip);
102 | position: absolute;
103 | left: 50%;
104 | top: -6px;
105 | transform: translateX(-50%) translateY(-100%);
106 | background: rgba(0, 0, 0, 0.7);
107 | text-align: center;
108 | color: #fff;
109 | font-size: 16px;
110 | min-width: 80px;
111 | border-radius: 5px;
112 | pointer-events: none;
113 | padding: 4px 8px;
114 | z-index: 99;
115 | opacity: 0;
116 | transition: 0.15s opacity;
117 | }
118 |
119 | [data-tooltip]:hover::after,
120 | [data-tooltip]:hover::before,
121 | [data-tooltip]:focus::after,
122 | [data-tooltip]:focus::before,
123 | [data-tooltip]:active::after,
124 | [data-tooltip]:active::before {
125 | opacity: 1;
126 | }
127 |
128 | [data-tooltip]:hover,
129 | [data-tooltip]:hover,
130 | [data-tooltip]:focus,
131 | [data-tooltip]:focus,
132 | [data-tooltip]:active,
133 | [data-tooltip]:active {
134 | outline: none;
135 | }
136 |
--------------------------------------------------------------------------------
/src/screens/user/components/profile.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import PropTypes from 'prop-types'
5 | import {Section, Text, Image} from '../../../shared/pattern'
6 | import UserContext from '../user-context'
7 |
8 | function Profile({user}) {
9 | return (
10 |
11 |
12 |
13 | {user.name}
14 |
19 | {user.login}
20 |
21 |
22 |
23 | {user.organizations.length ? (
24 |
25 | ) : null}
26 |
27 | )
28 | }
29 |
30 | Profile.propTypes = {
31 | user: PropTypes.shape({
32 | avatarUrl: PropTypes.string.isRequired,
33 | name: PropTypes.string.isRequired,
34 | login: PropTypes.string.isRequired,
35 | organizations: PropTypes.array.isRequired,
36 | }).isRequired,
37 | }
38 |
39 | function ProfileStatsSection({user}) {
40 | return (
41 |
46 | )
47 | }
48 |
49 | ProfileStatsSection.propTypes = {
50 | user: PropTypes.shape({
51 | followersCount: PropTypes.number,
52 | repositoriesCount: PropTypes.number,
53 | followingCount: PropTypes.number,
54 | }),
55 | }
56 |
57 | function ProfileStat({value, label}) {
58 | return (
59 |
65 |
66 | {value}
67 |
68 |
69 | {label}
70 |
71 |
72 | )
73 | }
74 |
75 | ProfileStat.propTypes = {
76 | value: PropTypes.number,
77 | label: PropTypes.string,
78 | }
79 |
80 | function OrganizationsSection({orgs}) {
81 | return (
82 |
83 | Organizations
84 | {orgs.map(org => (
85 |
86 |
96 |
97 | ))}
98 |
99 | )
100 | }
101 |
102 | OrganizationsSection.propTypes = {
103 | orgs: PropTypes.arrayOf(
104 | PropTypes.shape({
105 | id: PropTypes.string.isRequired,
106 | avatarUrl: PropTypes.string.isRequired,
107 | login: PropTypes.string.isRequired,
108 | }),
109 | ),
110 | }
111 |
112 | function ProfileUserConsumer() {
113 | return (
114 |
115 | {user => }
116 |
117 | )
118 | }
119 |
120 | export default ProfileUserConsumer
121 |
122 | /*
123 | eslint
124 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
125 | react/react-in-jsx-scope: "off"
126 | */
127 |
--------------------------------------------------------------------------------
/src/github-client.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import React from 'react'
5 | import {navigate, createHistory} from '@reach/router'
6 | import netlify from 'netlify-auth-providers'
7 | import {GraphQLClient} from 'graphql-request'
8 | import {PrimaryButton} from './shared/pattern'
9 |
10 | const GitHubClientContext = React.createContext()
11 | const {Provider, Consumer} = GitHubClientContext
12 |
13 | async function authWithGitHub() {
14 | return new Promise((resolve, reject) => {
15 | var authenticator = new netlify({
16 | site_id: process.env.REACT_APP_NETLIFY_SITE_ID,
17 | })
18 | authenticator.authenticate(
19 | {provider: 'github', scope: 'public_repo,read:org,read:user'},
20 | function(err, data) {
21 | if (err) {
22 | reject(err)
23 | }
24 | resolve(data)
25 | },
26 | )
27 | })
28 | }
29 |
30 | const history = createHistory(window)
31 |
32 | class GitHubClientProvider extends React.Component {
33 | constructor(...args) {
34 | super(...args)
35 | this.state = {error: null}
36 | if (this.props.client) {
37 | this.state.client = this.props.client
38 | } else {
39 | const token = window.localStorage.getItem('github-token')
40 | if (token) {
41 | this.state.client = this.getClient(token)
42 | }
43 | }
44 | }
45 | componentDidMount() {
46 | if (!this.state.client) {
47 | navigate('/')
48 | }
49 | this.unsubscribeHistory = history.listen(() => {
50 | if (!this.state.client) {
51 | navigate('/')
52 | }
53 | })
54 | }
55 | componentWillUnmount() {
56 | this.unsubscribeHistory()
57 | }
58 | getClient = token => {
59 | const headers = {Authorization: `bearer ${token}`}
60 | const client = new GraphQLClient('https://api.github.com/graphql', {
61 | headers,
62 | })
63 | return Object.assign(client, {
64 | login: this.login,
65 | logout: this.logout,
66 | })
67 | }
68 | logout = () => {
69 | window.localStorage.removeItem('github-token')
70 | this.setState({client: null, error: null})
71 | navigate('/')
72 | }
73 | login = async () => {
74 | const data = await authWithGitHub().catch(error => {
75 | console.log('Oh no', error)
76 | this.setState({error})
77 | })
78 | window.localStorage.setItem('github-token', data.token)
79 | this.setState({client: this.getClient(data.token)})
80 | }
81 | render() {
82 | const {client, error} = this.state
83 | const {children} = this.props
84 |
85 | return client ? (
86 | {children}
87 | ) : (
88 |
95 | {error ? (
96 |
97 |
Oh no! There was an error.
98 |
{JSON.stringify(error, null, 2)}
99 |
100 | ) : (
101 |
102 |
103 | Login with GitHub
104 |
105 |
106 | )}
107 |
108 | )
109 | }
110 | }
111 |
112 | export {
113 | GitHubClientProvider as Provider,
114 | Consumer,
115 | GitHubClientContext as Context,
116 | }
117 |
118 | /*
119 | eslint
120 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
121 | react/react-in-jsx-scope: "off"
122 | */
123 |
--------------------------------------------------------------------------------
/src/screens/user/components/__tests__/query.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {render as rtlRender, wait} from 'react-testing-library'
3 | import * as GitHubClient from '../../../../github-client'
4 | import Query from '../query'
5 |
6 | const fakeResponse = {fakeData: {}}
7 | const fakeClient = {request: jest.fn(() => Promise.resolve(fakeResponse))}
8 |
9 | beforeEach(() => {
10 | fakeClient.request.mockClear()
11 | })
12 |
13 | function renderQuery({
14 | client = fakeClient,
15 | children = jest.fn(() => null),
16 | query = '',
17 | variables = {},
18 | normalize,
19 | ...options
20 | } = {}) {
21 | const props = {query, variables, children, normalize}
22 | const utils = rtlRender(
23 |
24 |
25 | ,
26 | options,
27 | )
28 | return {
29 | ...utils,
30 | rerender: options =>
31 | renderQuery({
32 | container: utils.container,
33 | children,
34 | query,
35 | variables,
36 | normalize,
37 | ...options,
38 | }),
39 | client,
40 | query,
41 | variables,
42 | children,
43 | }
44 | }
45 |
46 | test('query makes requests to the client on mount', async () => {
47 | const {children, client, variables, query} = renderQuery()
48 | expect(children).toHaveBeenCalledTimes(2)
49 | expect(children).toHaveBeenCalledWith({
50 | data: null,
51 | error: null,
52 | fetching: true,
53 | loaded: false,
54 | })
55 | expect(client.request).toHaveBeenCalledTimes(1)
56 | expect(client.request).toHaveBeenCalledWith(query, variables)
57 |
58 | children.mockClear()
59 | await wait()
60 |
61 | expect(children).toHaveBeenCalledTimes(1)
62 | expect(children).toHaveBeenCalledWith({
63 | data: fakeResponse,
64 | error: null,
65 | fetching: false,
66 | loaded: true,
67 | })
68 | })
69 |
70 | test('does not request if rerendered and nothing changed', async () => {
71 | const {children, client, rerender} = renderQuery()
72 | await wait()
73 | children.mockClear()
74 | client.request.mockClear()
75 | rerender()
76 | await wait()
77 | expect(client.request).toHaveBeenCalledTimes(0)
78 | expect(children).toHaveBeenCalledTimes(1) // does still re-render children.
79 | })
80 |
81 | test('makes request if rerendered with new variables', async () => {
82 | const {client, query, rerender} = renderQuery({
83 | variables: {username: 'fred'},
84 | })
85 | await wait()
86 | client.request.mockClear()
87 | const newVariables = {username: 'george'}
88 | rerender({variables: newVariables})
89 | await wait()
90 | expect(client.request).toHaveBeenCalledTimes(1)
91 | expect(client.request).toHaveBeenCalledWith(query, newVariables)
92 | })
93 |
94 | test('makes request if rerendered with new query', async () => {
95 | const {client, variables, rerender} = renderQuery({
96 | query: `query neat() {}`,
97 | })
98 | await wait()
99 | client.request.mockClear()
100 | const newQuery = `query nice() {}`
101 | rerender({query: newQuery})
102 | await wait()
103 | expect(client.request).toHaveBeenCalledTimes(1)
104 | expect(client.request).toHaveBeenCalledWith(newQuery, variables)
105 | })
106 |
107 | test('normalize allows modifying data', async () => {
108 | const normalize = data => ({normalizedData: data})
109 | const {children} = renderQuery({normalize})
110 | await wait()
111 | expect(children).toHaveBeenCalledWith({
112 | data: {normalizedData: fakeResponse},
113 | error: null,
114 | fetching: false,
115 | loaded: true,
116 | })
117 | })
118 |
--------------------------------------------------------------------------------
/src/screens/user/index.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import {Component} from 'react'
5 | import PropTypes from 'prop-types'
6 | import {Container, Row, Column} from '../../shared/layout'
7 | import {
8 | Text,
9 | PrimaryButton,
10 | IsolatedContainer,
11 | ButtonLink,
12 | LoadingMessagePage,
13 | } from '../../shared/pattern'
14 | import {Context as GitHubContext} from '../../github-client'
15 | import Query from './components/query'
16 | import Profile from './components/profile'
17 | import RepoFilter from './components/repo-filter'
18 | import RepoList from './components/repo-list'
19 | import UserContext from './user-context'
20 |
21 | // this allows prettier to format things without changing the string contents
22 | const gql = String.raw
23 |
24 | const userQuery = gql`
25 | query getUserData($username: String!) {
26 | user(login: $username) {
27 | name
28 | login
29 | avatarUrl
30 | followers {
31 | totalCount
32 | }
33 | following {
34 | totalCount
35 | }
36 | repositories(
37 | privacy: PUBLIC
38 | first: 100
39 | isFork: false
40 | ownerAffiliations: [COLLABORATOR, OWNER]
41 | orderBy: {field: PUSHED_AT, direction: DESC}
42 | ) {
43 | totalCount
44 | edges {
45 | node {
46 | id
47 | name
48 | description
49 | url
50 | pushedAt
51 | stargazers {
52 | totalCount
53 | }
54 | forkCount
55 | languages(first: 1) {
56 | edges {
57 | node {
58 | name
59 | }
60 | }
61 | }
62 | }
63 | }
64 | }
65 | organizations(first: 100) {
66 | edges {
67 | node {
68 | avatarUrl
69 | id
70 | login
71 | url
72 | }
73 | }
74 | }
75 | }
76 | }
77 | `
78 |
79 | function normalizeUserData(data) {
80 | const {
81 | user: {
82 | name,
83 | login,
84 | avatarUrl,
85 | followers: {totalCount: followersCount},
86 | following: {totalCount: followingCount},
87 | repositories: {totalCount: repositoriesCount, edges: reposData},
88 | organizations: {edges: orgsData},
89 | },
90 | } = data
91 | const repositories = reposData.map(r => ({
92 | ...r.node,
93 | languages: undefined,
94 | stargazersCount: r.node.stargazers.totalCount,
95 | language: r.node.languages.edges[0]
96 | ? r.node.languages.edges[0].node.name
97 | : 'Unknown',
98 | }))
99 | const organizations = orgsData.map(o => o.node)
100 | return {
101 | name,
102 | login,
103 | avatarUrl,
104 | followersCount,
105 | followingCount,
106 | repositoriesCount,
107 | repositories,
108 | organizations,
109 | }
110 | }
111 |
112 | class User extends Component {
113 | static propTypes = {
114 | username: PropTypes.string,
115 | }
116 | static contextType = GitHubContext
117 | state = {filter: ''}
118 |
119 | handleFilterUpdate = filter => {
120 | this.setState({filter})
121 | }
122 |
123 | render() {
124 | const {username} = this.props
125 | const {filter} = this.state
126 | return (
127 |
132 | {({fetching, data, error}) =>
133 | error ? (
134 |
135 | There was an error loading the data
136 | {JSON.stringify(error, null, 2)}
137 |
138 | ) : fetching ? (
139 | Loading data for {username}
140 | ) : data ? (
141 |
142 |
143 |
144 |
145 |
146 |
150 | Logout
151 |
152 |
153 | Try another
154 |
155 |
156 |
157 | Repositories
158 |
162 |
163 |
164 |
165 |
166 |
167 | ) : (
168 | I have no idea what's up...
169 | )
170 | }
171 |
172 | )
173 | }
174 | }
175 |
176 | export default User
177 |
178 | /*
179 | eslint
180 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
181 | react/react-in-jsx-scope: "off"
182 | */
183 |
--------------------------------------------------------------------------------
/src/shared/pattern.js:
--------------------------------------------------------------------------------
1 | /* @jsx jsx */
2 | import {jsx} from '@emotion/core'
3 |
4 | import styled from '@emotion/styled/macro'
5 | import {Link} from '@reach/router'
6 | import Loading from './loading'
7 |
8 | export const Section = styled.div(
9 | {padding: '20px 0'},
10 | ({theme}) => theme.common.borderBottom,
11 | )
12 |
13 | const heading = {
14 | display: 'block',
15 | fontFamily: 'inherit',
16 | fontWeight: '500',
17 | lineHeight: '1.1',
18 | }
19 | const largerHeading = {
20 | marginTop: '20px',
21 | marginBottom: '10px',
22 | }
23 |
24 | const smallerHeading = {
25 | marginTop: '10px',
26 | marginBottom: '10px',
27 | }
28 |
29 | export const Text = styled.span(
30 | variantStyles({
31 | tint: {
32 | faded: ({theme}) => ({color: theme.colors.faded}),
33 | fadedExtra: ({theme}) => ({color: theme.colors.fadedExtra}),
34 | },
35 | size: {
36 | superheading: [heading, largerHeading, {fontSize: 36}],
37 | heading: [heading, largerHeading, {fontSize: 30}],
38 | subheading: [heading, largerHeading, {fontSize: 24}],
39 | superstandard: [heading, smallerHeading, {fontSize: 18}],
40 | standard: [heading, smallerHeading, {fontSize: 14}],
41 | substandard: [heading, smallerHeading, {fontSize: 12}],
42 | },
43 | }),
44 | )
45 |
46 | export const Input = styled.input({
47 | display: 'block',
48 | width: '100%',
49 | height: '34px',
50 | padding: '6px 12px',
51 | fontSize: '14px',
52 | lineHeight: '1.42857143',
53 | color: '#555',
54 | backgroundColor: '#fff',
55 | backgroundImage: 'none',
56 | border: '1px solid #ccc',
57 | borderRadius: '4px',
58 | boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, .075)',
59 | transition: 'border-color ease-in-out .15s, box-shadow ease-in-out .15s',
60 | ':focus': {
61 | borderColor: '#66afe9',
62 | outline: '0',
63 | boxShadow:
64 | 'inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6)',
65 | },
66 | })
67 |
68 | export const Button = styled.button({
69 | display: 'inline-block',
70 | padding: '6px 12px',
71 | marginBottom: '0',
72 | fontSize: '14px',
73 | fontWeight: 'normal',
74 | lineHeight: '1.42857143',
75 | textAlign: 'center',
76 | whiteSpace: 'nowrap',
77 | verticalAlign: 'middle',
78 | touchAction: 'manipulation',
79 | cursor: 'pointer',
80 | userSelect: 'none',
81 | backgroundImage: 'none',
82 | border: '1px solid transparent',
83 | borderRadius: '4px',
84 | textDecoration: 'none',
85 | color: '#333',
86 | })
87 |
88 | export const PrimaryButton = styled(Button)({
89 | color: '#fff',
90 | backgroundColor: '#337ab7',
91 | borderColor: '#2e6da4',
92 | '&:hover,&:active,&:focus': {
93 | color: '#fff',
94 | backgroundColor: '#286090',
95 | borderColor: '#204d74',
96 | },
97 | ':focus': {
98 | borderColor: '#122b40',
99 | },
100 | })
101 |
102 | export const ButtonLink = Button.withComponent(Link)
103 | export const PrimaryButtonLink = PrimaryButton.withComponent(Link)
104 |
105 | export const Image = styled.img(
106 | {border: '0', verticalAlign: 'middle'},
107 | propStyles({
108 | responsive: {
109 | display: 'block',
110 | maxWidth: '100%',
111 | height: 'auto',
112 | },
113 | rounded: {
114 | borderRadius: '6px',
115 | },
116 | }),
117 | )
118 |
119 | export const Anchor = styled.a({
120 | color: '#337ab7',
121 | textDecoration: 'none',
122 | '&:active,&:hover': {outline: 0},
123 | '&:hover,&:focus': {
124 | color: '#23527c',
125 | textDecoration: 'underline',
126 | },
127 | ':focus': {
128 | outline: '5px auto -webkit-focus-ring-color',
129 | outlineOffset: '-2px',
130 | },
131 | })
132 |
133 | export function IsolatedContainer({children, ...props}) {
134 | return (
135 |
145 | )
146 | }
147 |
148 | export function LoadingMessagePage({children}) {
149 | return (
150 |
151 |
152 |
153 | {children}
154 |
155 |
156 |
157 |
158 | )
159 | }
160 |
161 | /**
162 | * Makes it easier to create an emotion component which
163 | * accepts props to enable/disable certain styles.
164 | *
165 | * accepts an object where the key is a prop and the value
166 | * is the styles that should be applied if that prop is
167 | * passed. Returns a function which you pass to a
168 | * emotionComponentFactory.
169 | *
170 | * @param {Object} styles The prop to styles object
171 | * @return {Function} the dynamic styles function
172 | */
173 | function propStyles(styles) {
174 | return function dynamicStyles(props) {
175 | return Object.keys(props).map(key => {
176 | if (styles[key]) {
177 | return applyStyles(styles[key], props)
178 | }
179 | return null
180 | })
181 | }
182 | }
183 |
184 | /**
185 | * Makes it easier to create an emotion component
186 | * which accepts enums for certain variants of styles
187 | *
188 | * Accepts an object where the key is a variant name
189 | * (the prop consumers will use) and the value is an
190 | * object where those keys are the possible values for
191 | * the variant prop, and the value is the styles to be
192 | * applied.
193 | */
194 | function variantStyles(styles) {
195 | return function dynamicStyles(props) {
196 | return Object.entries(props).map(([key, value]) => {
197 | if (styles[key]) {
198 | return applyStyles(styles[key][value], props)
199 | }
200 | return null
201 | })
202 | }
203 | }
204 |
205 | function applyStyles(styles, props) {
206 | return typeof styles === 'function'
207 | ? styles(props)
208 | : Array.isArray(styles)
209 | ? styles.map(s => applyStyles(s, props))
210 | : styles
211 | }
212 |
213 | /*
214 | eslint
215 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}]
216 | react/react-in-jsx-scope: "off"
217 | */
218 |
--------------------------------------------------------------------------------
/src/docs/mock-query-response.json:
--------------------------------------------------------------------------------
1 | {
2 | "user": {
3 | "name": "Kent C. Dodds",
4 | "login": "kentcdodds",
5 | "avatarUrl": "https://avatars0.githubusercontent.com/u/1500684?v=4",
6 | "followers": {
7 | "totalCount": 7046
8 | },
9 | "following": {
10 | "totalCount": 38
11 | },
12 | "repositories": {
13 | "totalCount": 515,
14 | "edges": [
15 | {
16 | "node": {
17 | "id": "MDEwOlJlcG9zaXRvcnk5ODMxNTczNQ==",
18 | "name": "downshift",
19 | "description": "🏎 Primitive to build simple, flexible, WAI-ARIA compliant enhanced input React components",
20 | "url": "https://github.com/paypal/downshift",
21 | "pushedAt": "2018-11-08T19:49:21Z",
22 | "stargazers": {
23 | "totalCount": 5794
24 | },
25 | "forkCount": 422,
26 | "languages": {
27 | "edges": [
28 | {
29 | "node": {
30 | "name": "JavaScript"
31 | }
32 | }
33 | ]
34 | }
35 | }
36 | },
37 | {
38 | "node": {
39 | "id": "MDEwOlJlcG9zaXRvcnkxMjg0MDU5MTU=",
40 | "name": "cypress-testing-library",
41 | "description": "🐅 Simple and complete custom Cypress commands and utilities that encourage good testing practices.",
42 | "url": "https://github.com/kentcdodds/cypress-testing-library",
43 | "pushedAt": "2018-11-08T19:44:09Z",
44 | "stargazers": {
45 | "totalCount": 264
46 | },
47 | "forkCount": 18,
48 | "languages": {
49 | "edges": [
50 | {
51 | "node": {
52 | "name": "JavaScript"
53 | }
54 | }
55 | ]
56 | }
57 | }
58 | },
59 | {
60 | "node": {
61 | "id": "MDEwOlJlcG9zaXRvcnk3MjI2Mzg2MQ==",
62 | "name": "react-github-profile",
63 | "description": null,
64 | "url": "https://github.com/kentcdodds/react-github-profile",
65 | "pushedAt": "2018-11-08T19:05:53Z",
66 | "stargazers": {
67 | "totalCount": 46
68 | },
69 | "forkCount": 3,
70 | "languages": {
71 | "edges": [
72 | {
73 | "node": {
74 | "name": "HTML"
75 | }
76 | }
77 | ]
78 | }
79 | }
80 | },
81 | {
82 | "node": {
83 | "id": "MDEwOlJlcG9zaXRvcnk4OTI5MDYwMQ==",
84 | "name": "theming",
85 | "description": "Unified CSSinJS theming solution for React",
86 | "url": "https://github.com/cssinjs/theming",
87 | "pushedAt": "2018-11-08T12:37:01Z",
88 | "stargazers": {
89 | "totalCount": 233
90 | },
91 | "forkCount": 29,
92 | "languages": {
93 | "edges": [
94 | {
95 | "node": {
96 | "name": "JavaScript"
97 | }
98 | }
99 | ]
100 | }
101 | }
102 | },
103 | {
104 | "node": {
105 | "id": "MDEwOlJlcG9zaXRvcnkxNTY2NDEzNzA=",
106 | "name": "babel-blade",
107 | "description": "a Babel plugin/macro that solves the Double Declaration problem in GraphQL. Please see Docs at:",
108 | "url": "https://github.com/kentcdodds/babel-blade",
109 | "pushedAt": "2018-11-08T02:54:26Z",
110 | "stargazers": {
111 | "totalCount": 0
112 | },
113 | "forkCount": 0,
114 | "languages": {
115 | "edges": [
116 | {
117 | "node": {
118 | "name": "JavaScript"
119 | }
120 | }
121 | ]
122 | }
123 | }
124 | },
125 | {
126 | "node": {
127 | "id": "MDEwOlJlcG9zaXRvcnk1MjcxMDA2NQ==",
128 | "name": "all-contributors",
129 | "description": "✨ Recognize all contributors, not just the ones who push code ✨",
130 | "url": "https://github.com/kentcdodds/all-contributors",
131 | "pushedAt": "2018-11-07T20:00:55Z",
132 | "stargazers": {
133 | "totalCount": 2077
134 | },
135 | "forkCount": 294,
136 | "languages": {
137 | "edges": [
138 | {
139 | "node": {
140 | "name": "JavaScript"
141 | }
142 | }
143 | ]
144 | }
145 | }
146 | },
147 | {
148 | "node": {
149 | "id": "MDEwOlJlcG9zaXRvcnkxMjgzOTYwNzY=",
150 | "name": "dom-testing-library",
151 | "description": "🐙 Simple and complete DOM testing utilities that encourage good testing practices.",
152 | "url": "https://github.com/kentcdodds/dom-testing-library",
153 | "pushedAt": "2018-11-07T18:21:49Z",
154 | "stargazers": {
155 | "totalCount": 739
156 | },
157 | "forkCount": 83,
158 | "languages": {
159 | "edges": [
160 | {
161 | "node": {
162 | "name": "JavaScript"
163 | }
164 | }
165 | ]
166 | }
167 | }
168 | },
169 | {
170 | "node": {
171 | "id": "MDEwOlJlcG9zaXRvcnkxMjU4NjM1MDc=",
172 | "name": "react-testing-library",
173 | "description": "🐐 Simple and complete React DOM testing utilities that encourage good testing practices.",
174 | "url": "https://github.com/kentcdodds/react-testing-library",
175 | "pushedAt": "2018-11-06T22:13:45Z",
176 | "stargazers": {
177 | "totalCount": 3775
178 | },
179 | "forkCount": 222,
180 | "languages": {
181 | "edges": [
182 | {
183 | "node": {
184 | "name": "JavaScript"
185 | }
186 | }
187 | ]
188 | }
189 | }
190 | },
191 | {
192 | "node": {
193 | "id": "MDEwOlJlcG9zaXRvcnk5MTQ3NzkwMw==",
194 | "name": "babel-plugin-tester",
195 | "description": "Utilities for testing babel plugins",
196 | "url": "https://github.com/babel-utils/babel-plugin-tester",
197 | "pushedAt": "2018-11-06T21:49:37Z",
198 | "stargazers": {
199 | "totalCount": 114
200 | },
201 | "forkCount": 20,
202 | "languages": {
203 | "edges": [
204 | {
205 | "node": {
206 | "name": "JavaScript"
207 | }
208 | }
209 | ]
210 | }
211 | }
212 | },
213 | {
214 | "node": {
215 | "id": "MDEwOlJlcG9zaXRvcnk1Mjc0MjA0Mw==",
216 | "name": "all-contributors-cli",
217 | "description": "Tool to help automate adding contributor acknowledgements according to the all-contributors specification.",
218 | "url": "https://github.com/jfmengels/all-contributors-cli",
219 | "pushedAt": "2018-11-06T21:44:54Z",
220 | "stargazers": {
221 | "totalCount": 204
222 | },
223 | "forkCount": 60,
224 | "languages": {
225 | "edges": [
226 | {
227 | "node": {
228 | "name": "JavaScript"
229 | }
230 | }
231 | ]
232 | }
233 | }
234 | },
235 | {
236 | "node": {
237 | "id": "MDEwOlJlcG9zaXRvcnkxMDc3ODc2NQ==",
238 | "name": "kentcdodds.com",
239 | "description": "Kents Homepage",
240 | "url": "https://github.com/kentcdodds/kentcdodds.com",
241 | "pushedAt": "2018-11-06T19:46:39Z",
242 | "stargazers": {
243 | "totalCount": 13
244 | },
245 | "forkCount": 5,
246 | "languages": {
247 | "edges": [
248 | {
249 | "node": {
250 | "name": "JavaScript"
251 | }
252 | }
253 | ]
254 | }
255 | }
256 | },
257 | {
258 | "node": {
259 | "id": "MDEwOlJlcG9zaXRvcnkxMjg3Nzg2OTI=",
260 | "name": "jest-dom",
261 | "description": ":owl: Custom jest matchers to test the state of the DOM",
262 | "url": "https://github.com/gnapse/jest-dom",
263 | "pushedAt": "2018-11-06T13:55:42Z",
264 | "stargazers": {
265 | "totalCount": 188
266 | },
267 | "forkCount": 18,
268 | "languages": {
269 | "edges": [
270 | {
271 | "node": {
272 | "name": "JavaScript"
273 | }
274 | }
275 | ]
276 | }
277 | }
278 | },
279 | {
280 | "node": {
281 | "id": "MDEwOlJlcG9zaXRvcnkzNTcxMTU5NQ==",
282 | "name": "NG6-starter",
283 | "description": ":ng: An AngularJS Starter repo for AngularJS + ES6 + Webpack by @AngularClass a @OneSpeed-io company",
284 | "url": "https://github.com/gdi2290/NG6-starter",
285 | "pushedAt": "2018-11-04T23:35:20Z",
286 | "stargazers": {
287 | "totalCount": 1945
288 | },
289 | "forkCount": 1561,
290 | "languages": {
291 | "edges": [
292 | {
293 | "node": {
294 | "name": "JavaScript"
295 | }
296 | }
297 | ]
298 | }
299 | }
300 | },
301 | {
302 | "node": {
303 | "id": "MDEwOlJlcG9zaXRvcnk4NzEyMzE0Ng==",
304 | "name": "glamorous",
305 | "description": "💄 Maintainable CSS with React",
306 | "url": "https://github.com/paypal/glamorous",
307 | "pushedAt": "2018-11-03T23:55:38Z",
308 | "stargazers": {
309 | "totalCount": 3751
310 | },
311 | "forkCount": 282,
312 | "languages": {
313 | "edges": [
314 | {
315 | "node": {
316 | "name": "JavaScript"
317 | }
318 | }
319 | ]
320 | }
321 | }
322 | },
323 | {
324 | "node": {
325 | "id": "MDEwOlJlcG9zaXRvcnk0MzUxMjkxNA==",
326 | "name": "cross-env",
327 | "description": "🔀 Cross platform setting of environment scripts",
328 | "url": "https://github.com/kentcdodds/cross-env",
329 | "pushedAt": "2018-11-03T23:54:03Z",
330 | "stargazers": {
331 | "totalCount": 3123
332 | },
333 | "forkCount": 139,
334 | "languages": {
335 | "edges": [
336 | {
337 | "node": {
338 | "name": "JavaScript"
339 | }
340 | }
341 | ]
342 | }
343 | }
344 | },
345 | {
346 | "node": {
347 | "id": "MDEwOlJlcG9zaXRvcnk3ODg5MDczMQ==",
348 | "name": "prettier-eslint-cli",
349 | "description": "CLI for prettier-eslint",
350 | "url": "https://github.com/prettier/prettier-eslint-cli",
351 | "pushedAt": "2018-11-03T17:20:24Z",
352 | "stargazers": {
353 | "totalCount": 298
354 | },
355 | "forkCount": 52,
356 | "languages": {
357 | "edges": [
358 | {
359 | "node": {
360 | "name": "JavaScript"
361 | }
362 | }
363 | ]
364 | }
365 | }
366 | },
367 | {
368 | "node": {
369 | "id": "MDEwOlJlcG9zaXRvcnk0MjI1OTYxOA==",
370 | "name": "node-manage-path",
371 | "description": "This micro-lib allows you to alter the $PATH in a cross-platform way.",
372 | "url": "https://github.com/kentcdodds/node-manage-path",
373 | "pushedAt": "2018-11-03T14:28:27Z",
374 | "stargazers": {
375 | "totalCount": 3
376 | },
377 | "forkCount": 0,
378 | "languages": {
379 | "edges": [
380 | {
381 | "node": {
382 | "name": "JavaScript"
383 | }
384 | }
385 | ]
386 | }
387 | }
388 | },
389 | {
390 | "node": {
391 | "id": "MDEwOlJlcG9zaXRvcnkyMTQ3Njc0Ng==",
392 | "name": "nz-toggle",
393 | "description": "Dual and triple-state toggles for AngularJS",
394 | "url": "https://github.com/tannerlinsley/nz-toggle",
395 | "pushedAt": "2018-10-31T13:45:17Z",
396 | "stargazers": {
397 | "totalCount": 36
398 | },
399 | "forkCount": 26,
400 | "languages": {
401 | "edges": [
402 | {
403 | "node": {
404 | "name": "CSS"
405 | }
406 | }
407 | ]
408 | }
409 | }
410 | },
411 | {
412 | "node": {
413 | "id": "MDEwOlJlcG9zaXRvcnk2NDY5OTQ5NA==",
414 | "name": "awesome-webpack",
415 | "description": "A curated list of awesome Webpack resources, libraries and tools",
416 | "url": "https://github.com/webpack-contrib/awesome-webpack",
417 | "pushedAt": "2018-10-31T11:09:42Z",
418 | "stargazers": {
419 | "totalCount": 2907
420 | },
421 | "forkCount": 224,
422 | "languages": {
423 | "edges": [
424 | {
425 | "node": {
426 | "name": "HTML"
427 | }
428 | }
429 | ]
430 | }
431 | }
432 | },
433 | {
434 | "node": {
435 | "id": "MDEwOlJlcG9zaXRvcnkzNTMxMjQ1OA==",
436 | "name": "nyc",
437 | "description": "the Istanbul command line interface",
438 | "url": "https://github.com/istanbuljs/nyc",
439 | "pushedAt": "2018-10-31T06:28:47Z",
440 | "stargazers": {
441 | "totalCount": 2993
442 | },
443 | "forkCount": 222,
444 | "languages": {
445 | "edges": [
446 | {
447 | "node": {
448 | "name": "JavaScript"
449 | }
450 | }
451 | ]
452 | }
453 | }
454 | },
455 | {
456 | "node": {
457 | "id": "MDEwOlJlcG9zaXRvcnk0MDQ1MDY3NQ==",
458 | "name": "firsttimersonly",
459 | "description": "The Repository for the FirstTimersOnly movement in Open Source. We want projects to reserve some issues for newbies.",
460 | "url": "https://github.com/shanselman/firsttimersonly",
461 | "pushedAt": "2018-10-30T15:13:40Z",
462 | "stargazers": {
463 | "totalCount": 251
464 | },
465 | "forkCount": 177,
466 | "languages": {
467 | "edges": [
468 | {
469 | "node": {
470 | "name": "Ruby"
471 | }
472 | }
473 | ]
474 | }
475 | }
476 | },
477 | {
478 | "node": {
479 | "id": "MDEwOlJlcG9zaXRvcnkxMDIwMjk2MTI=",
480 | "name": "react-toggled",
481 | "description": "Component to build simple, flexible, and accessible toggle components",
482 | "url": "https://github.com/kentcdodds/react-toggled",
483 | "pushedAt": "2018-10-25T21:50:39Z",
484 | "stargazers": {
485 | "totalCount": 374
486 | },
487 | "forkCount": 34,
488 | "languages": {
489 | "edges": [
490 | {
491 | "node": {
492 | "name": "JavaScript"
493 | }
494 | }
495 | ]
496 | }
497 | }
498 | },
499 | {
500 | "node": {
501 | "id": "MDEwOlJlcG9zaXRvcnkxNTQ3MDcwMTM=",
502 | "name": "react-hooks-and-suspense-egghead-playlist",
503 | "description": "This is the code for the egghead playlist \"React Hooks and Suspense\"",
504 | "url": "https://github.com/kentcdodds/react-hooks-and-suspense-egghead-playlist",
505 | "pushedAt": "2018-10-25T17:52:56Z",
506 | "stargazers": {
507 | "totalCount": 85
508 | },
509 | "forkCount": 4,
510 | "languages": {
511 | "edges": [
512 | {
513 | "node": {
514 | "name": "HTML"
515 | }
516 | }
517 | ]
518 | }
519 | }
520 | },
521 | {
522 | "node": {
523 | "id": "MDEwOlJlcG9zaXRvcnk3NzY1NTAxOA==",
524 | "name": "testing-workshop",
525 | "description": "A workshop for learning how to test JavaScript applications",
526 | "url": "https://github.com/kentcdodds/testing-workshop",
527 | "pushedAt": "2018-10-25T15:46:48Z",
528 | "stargazers": {
529 | "totalCount": 737
530 | },
531 | "forkCount": 308,
532 | "languages": {
533 | "edges": [
534 | {
535 | "node": {
536 | "name": "JavaScript"
537 | }
538 | }
539 | ]
540 | }
541 | }
542 | },
543 | {
544 | "node": {
545 | "id": "MDEwOlJlcG9zaXRvcnkxNTQ2MDA5NzU=",
546 | "name": "suspense-max-duration-issue",
547 | "description": null,
548 | "url": "https://github.com/kentcdodds/suspense-max-duration-issue",
549 | "pushedAt": "2018-10-25T04:15:10Z",
550 | "stargazers": {
551 | "totalCount": 2
552 | },
553 | "forkCount": 2,
554 | "languages": {
555 | "edges": [
556 | {
557 | "node": {
558 | "name": "HTML"
559 | }
560 | }
561 | ]
562 | }
563 | }
564 | },
565 | {
566 | "node": {
567 | "id": "MDEwOlJlcG9zaXRvcnkxNDgyNDIxMjI=",
568 | "name": "static-testing-tools",
569 | "description": null,
570 | "url": "https://github.com/kentcdodds/static-testing-tools",
571 | "pushedAt": "2018-10-24T15:58:04Z",
572 | "stargazers": {
573 | "totalCount": 18
574 | },
575 | "forkCount": 2,
576 | "languages": {
577 | "edges": [
578 | {
579 | "node": {
580 | "name": "JavaScript"
581 | }
582 | }
583 | ]
584 | }
585 | }
586 | },
587 | {
588 | "node": {
589 | "id": "MDEwOlJlcG9zaXRvcnkxMDM4NjQ4Njg=",
590 | "name": "import-all.macro",
591 | "description": "A babel-macro that allows you to import all files that match a glob",
592 | "url": "https://github.com/kentcdodds/import-all.macro",
593 | "pushedAt": "2018-10-23T22:23:28Z",
594 | "stargazers": {
595 | "totalCount": 73
596 | },
597 | "forkCount": 9,
598 | "languages": {
599 | "edges": [
600 | {
601 | "node": {
602 | "name": "JavaScript"
603 | }
604 | }
605 | ]
606 | }
607 | }
608 | },
609 | {
610 | "node": {
611 | "id": "MDEwOlJlcG9zaXRvcnk0MTcxODE4NQ==",
612 | "name": "awesome-angular",
613 | "description": ":page_facing_up: A curated list of awesome Angular resources by @TipeIO",
614 | "url": "https://github.com/gdi2290/awesome-angular",
615 | "pushedAt": "2018-10-23T07:08:18Z",
616 | "stargazers": {
617 | "totalCount": 6078
618 | },
619 | "forkCount": 917,
620 | "languages": {
621 | "edges": [
622 | {
623 | "node": {
624 | "name": "HTML"
625 | }
626 | }
627 | ]
628 | }
629 | }
630 | },
631 | {
632 | "node": {
633 | "id": "MDEwOlJlcG9zaXRvcnkxNDM0ODI1MjA=",
634 | "name": "simply-react",
635 | "description": null,
636 | "url": "https://github.com/kentcdodds/simply-react",
637 | "pushedAt": "2018-10-20T13:07:13Z",
638 | "stargazers": {
639 | "totalCount": 60
640 | },
641 | "forkCount": 2,
642 | "languages": {
643 | "edges": [
644 | {
645 | "node": {
646 | "name": "JavaScript"
647 | }
648 | }
649 | ]
650 | }
651 | }
652 | },
653 | {
654 | "node": {
655 | "id": "MDEwOlJlcG9zaXRvcnkxMzcwMjAzMTI=",
656 | "name": "js-testing-fundamentals",
657 | "description": null,
658 | "url": "https://github.com/kentcdodds/js-testing-fundamentals",
659 | "pushedAt": "2018-10-19T14:41:50Z",
660 | "stargazers": {
661 | "totalCount": 93
662 | },
663 | "forkCount": 10,
664 | "languages": {
665 | "edges": [
666 | {
667 | "node": {
668 | "name": "JavaScript"
669 | }
670 | }
671 | ]
672 | }
673 | }
674 | },
675 | {
676 | "node": {
677 | "id": "MDEwOlJlcG9zaXRvcnkxNDEzMTM4MjY=",
678 | "name": "setup-prettier",
679 | "description": null,
680 | "url": "https://github.com/kentcdodds/setup-prettier",
681 | "pushedAt": "2018-10-18T13:36:55Z",
682 | "stargazers": {
683 | "totalCount": 37
684 | },
685 | "forkCount": 3,
686 | "languages": {
687 | "edges": [
688 | {
689 | "node": {
690 | "name": "JavaScript"
691 | }
692 | }
693 | ]
694 | }
695 | }
696 | },
697 | {
698 | "node": {
699 | "id": "MDEwOlJlcG9zaXRvcnk5NjQ4NDAzMQ==",
700 | "name": "babel-plugin-macros",
701 | "description": "🎣 Enables zero-config, importable babel plugins",
702 | "url": "https://github.com/kentcdodds/babel-plugin-macros",
703 | "pushedAt": "2018-10-18T09:05:33Z",
704 | "stargazers": {
705 | "totalCount": 908
706 | },
707 | "forkCount": 63,
708 | "languages": {
709 | "edges": [
710 | {
711 | "node": {
712 | "name": "JavaScript"
713 | }
714 | }
715 | ]
716 | }
717 | }
718 | },
719 | {
720 | "node": {
721 | "id": "MDEwOlJlcG9zaXRvcnkzMTgyOTc3MA==",
722 | "name": "angular-starter",
723 | "description": ":tada: An Angular Starter kit featuring Angular (Router, Http, Forms, Services, Tests, E2E, Dev/Prod, HMR, Async/Lazy Routes, AoT via ngc), Karma, Protractor, Jasmine, Istanbul, TypeScript, TsLint, Codelyzer, Hot Module Replacement, @types, and Webpack by @TipeIO",
724 | "url": "https://github.com/gdi2290/angular-starter",
725 | "pushedAt": "2018-10-18T01:39:44Z",
726 | "stargazers": {
727 | "totalCount": 10108
728 | },
729 | "forkCount": 5476,
730 | "languages": {
731 | "edges": [
732 | {
733 | "node": {
734 | "name": "HTML"
735 | }
736 | }
737 | ]
738 | }
739 | }
740 | },
741 | {
742 | "node": {
743 | "id": "MDEwOlJlcG9zaXRvcnkxMzgyNTc4Nzg=",
744 | "name": "jest-cypress-react-babel-webpack",
745 | "description": "See how to configure Jest and Cypress with React, Babel, and Webpack",
746 | "url": "https://github.com/kentcdodds/jest-cypress-react-babel-webpack",
747 | "pushedAt": "2018-10-16T15:22:29Z",
748 | "stargazers": {
749 | "totalCount": 76
750 | },
751 | "forkCount": 15,
752 | "languages": {
753 | "edges": [
754 | {
755 | "node": {
756 | "name": "JavaScript"
757 | }
758 | }
759 | ]
760 | }
761 | }
762 | },
763 | {
764 | "node": {
765 | "id": "MDEwOlJlcG9zaXRvcnkxMjc3NTQ3Njg=",
766 | "name": "advanced-react-patterns-v2",
767 | "description": "Created with CodeSandbox",
768 | "url": "https://github.com/kentcdodds/advanced-react-patterns-v2",
769 | "pushedAt": "2018-10-14T20:40:08Z",
770 | "stargazers": {
771 | "totalCount": 955
772 | },
773 | "forkCount": 320,
774 | "languages": {
775 | "edges": [
776 | {
777 | "node": {
778 | "name": "HTML"
779 | }
780 | }
781 | ]
782 | }
783 | }
784 | },
785 | {
786 | "node": {
787 | "id": "MDEwOlJlcG9zaXRvcnkxMTQyNzE1MDU=",
788 | "name": "awesome-react-render-props",
789 | "description": "Awesome list of React components with render props",
790 | "url": "https://github.com/jaredpalmer/awesome-react-render-props",
791 | "pushedAt": "2018-10-14T01:30:08Z",
792 | "stargazers": {
793 | "totalCount": 1042
794 | },
795 | "forkCount": 77,
796 | "languages": {
797 | "edges": []
798 | }
799 | }
800 | },
801 | {
802 | "node": {
803 | "id": "MDEwOlJlcG9zaXRvcnk3Mzk0OTQ4Ng==",
804 | "name": "rtl-css-js",
805 | "description": "RTL for CSS in JS",
806 | "url": "https://github.com/kentcdodds/rtl-css-js",
807 | "pushedAt": "2018-10-11T18:45:46Z",
808 | "stargazers": {
809 | "totalCount": 63
810 | },
811 | "forkCount": 15,
812 | "languages": {
813 | "edges": [
814 | {
815 | "node": {
816 | "name": "JavaScript"
817 | }
818 | }
819 | ]
820 | }
821 | }
822 | },
823 | {
824 | "node": {
825 | "id": "MDEwOlJlcG9zaXRvcnkxMDIyNDQ3MjI=",
826 | "name": "kcd-scripts",
827 | "description": "CLI toolbox for common scripts for my projects",
828 | "url": "https://github.com/kentcdodds/kcd-scripts",
829 | "pushedAt": "2018-10-11T16:49:11Z",
830 | "stargazers": {
831 | "totalCount": 340
832 | },
833 | "forkCount": 81,
834 | "languages": {
835 | "edges": [
836 | {
837 | "node": {
838 | "name": "JavaScript"
839 | }
840 | }
841 | ]
842 | }
843 | }
844 | },
845 | {
846 | "node": {
847 | "id": "MDEwOlJlcG9zaXRvcnk4OTY0MDQ4MQ==",
848 | "name": "css-in-js-precompiler",
849 | "description": "WORK IN PROGRESS: Precompiles CSS-in-JS objects to CSS strings",
850 | "url": "https://github.com/kentcdodds/css-in-js-precompiler",
851 | "pushedAt": "2018-10-09T18:58:55Z",
852 | "stargazers": {
853 | "totalCount": 72
854 | },
855 | "forkCount": 3,
856 | "languages": {
857 | "edges": [
858 | {
859 | "node": {
860 | "name": "JavaScript"
861 | }
862 | }
863 | ]
864 | }
865 | }
866 | },
867 | {
868 | "node": {
869 | "id": "MDEwOlJlcG9zaXRvcnk5NzI1OTU5OQ==",
870 | "name": "next.js",
871 | "description": "Framework for server-rendered React apps",
872 | "url": "https://github.com/kentcdodds/next.js",
873 | "pushedAt": "2018-10-09T14:59:32Z",
874 | "stargazers": {
875 | "totalCount": 0
876 | },
877 | "forkCount": 0,
878 | "languages": {
879 | "edges": [
880 | {
881 | "node": {
882 | "name": "JavaScript"
883 | }
884 | }
885 | ]
886 | }
887 | }
888 | },
889 | {
890 | "node": {
891 | "id": "MDEwOlJlcG9zaXRvcnk3NzY1NTA0OQ==",
892 | "name": "asts-workshop",
893 | "description": "Improved productivity 💯 with the practical 🤓 use of the power 💪 of Abstract Syntax Trees 🌳 to lint ⚠️ and transform 🔀 your code",
894 | "url": "https://github.com/kentcdodds/asts-workshop",
895 | "pushedAt": "2018-10-07T15:26:54Z",
896 | "stargazers": {
897 | "totalCount": 134
898 | },
899 | "forkCount": 25,
900 | "languages": {
901 | "edges": [
902 | {
903 | "node": {
904 | "name": "JavaScript"
905 | }
906 | }
907 | ]
908 | }
909 | }
910 | },
911 | {
912 | "node": {
913 | "id": "MDEwOlJlcG9zaXRvcnk1NzUyMTg2Mg==",
914 | "name": "generator-kcd-oss",
915 | "description": "A yeoman generator for my open source modules",
916 | "url": "https://github.com/kentcdodds/generator-kcd-oss",
917 | "pushedAt": "2018-10-07T01:36:40Z",
918 | "stargazers": {
919 | "totalCount": 59
920 | },
921 | "forkCount": 15,
922 | "languages": {
923 | "edges": [
924 | {
925 | "node": {
926 | "name": "JavaScript"
927 | }
928 | }
929 | ]
930 | }
931 | }
932 | },
933 | {
934 | "node": {
935 | "id": "MDEwOlJlcG9zaXRvcnkxNTE2NDM4MjQ=",
936 | "name": "reach-ui",
937 | "description": "The Accessible Foundation for React Apps and Design Systems.",
938 | "url": "https://github.com/kentcdodds/reach-ui",
939 | "pushedAt": "2018-10-05T22:09:16Z",
940 | "stargazers": {
941 | "totalCount": 0
942 | },
943 | "forkCount": 0,
944 | "languages": {
945 | "edges": [
946 | {
947 | "node": {
948 | "name": "JavaScript"
949 | }
950 | }
951 | ]
952 | }
953 | }
954 | },
955 | {
956 | "node": {
957 | "id": "MDEwOlJlcG9zaXRvcnkzOTc3NDQ4OQ==",
958 | "name": "up-for-grabs.net",
959 | "description": "Jump in!",
960 | "url": "https://github.com/kentcdodds/up-for-grabs.net",
961 | "pushedAt": "2018-10-05T16:19:13Z",
962 | "stargazers": {
963 | "totalCount": 0
964 | },
965 | "forkCount": 0,
966 | "languages": {
967 | "edges": [
968 | {
969 | "node": {
970 | "name": "Ruby"
971 | }
972 | }
973 | ]
974 | }
975 | }
976 | },
977 | {
978 | "node": {
979 | "id": "MDEwOlJlcG9zaXRvcnk3ODgxOTE5MA==",
980 | "name": "prettier-eslint",
981 | "description": "Code :arrow_right: prettier :arrow_right: eslint --fix :arrow_right: Formatted Code :sparkles:",
982 | "url": "https://github.com/prettier/prettier-eslint",
983 | "pushedAt": "2018-10-05T13:52:32Z",
984 | "stargazers": {
985 | "totalCount": 1974
986 | },
987 | "forkCount": 116,
988 | "languages": {
989 | "edges": [
990 | {
991 | "node": {
992 | "name": "JavaScript"
993 | }
994 | }
995 | ]
996 | }
997 | }
998 | },
999 | {
1000 | "node": {
1001 | "id": "MDEwOlJlcG9zaXRvcnkxNDg5MTQyNDc=",
1002 | "name": "dom-testing-library-with-anything",
1003 | "description": "you can use dom-testing-library with anything that renders to the DOM",
1004 | "url": "https://github.com/kentcdodds/dom-testing-library-with-anything",
1005 | "pushedAt": "2018-10-04T21:30:34Z",
1006 | "stargazers": {
1007 | "totalCount": 110
1008 | },
1009 | "forkCount": 20,
1010 | "languages": {
1011 | "edges": [
1012 | {
1013 | "node": {
1014 | "name": "JavaScript"
1015 | }
1016 | }
1017 | ]
1018 | }
1019 | }
1020 | },
1021 | {
1022 | "node": {
1023 | "id": "MDEwOlJlcG9zaXRvcnk1NzA3Mjk3MA==",
1024 | "name": "nps",
1025 | "description": ":100: All the benefits of npm scripts without the cost of a bloated package.json and limits of json :sparkles:",
1026 | "url": "https://github.com/kentcdodds/nps",
1027 | "pushedAt": "2018-10-04T16:48:49Z",
1028 | "stargazers": {
1029 | "totalCount": 954
1030 | },
1031 | "forkCount": 86,
1032 | "languages": {
1033 | "edges": [
1034 | {
1035 | "node": {
1036 | "name": "JavaScript"
1037 | }
1038 | }
1039 | ]
1040 | }
1041 | }
1042 | },
1043 | {
1044 | "node": {
1045 | "id": "MDEwOlJlcG9zaXRvcnkyNDMwMTk5Nw==",
1046 | "name": "react-example",
1047 | "description": "Simple React example app",
1048 | "url": "https://github.com/mzabriskie/react-example",
1049 | "pushedAt": "2018-10-02T10:15:33Z",
1050 | "stargazers": {
1051 | "totalCount": 153
1052 | },
1053 | "forkCount": 160,
1054 | "languages": {
1055 | "edges": [
1056 | {
1057 | "node": {
1058 | "name": "CSS"
1059 | }
1060 | }
1061 | ]
1062 | }
1063 | }
1064 | },
1065 | {
1066 | "node": {
1067 | "id": "MDEwOlJlcG9zaXRvcnk3ODQ5ODU2Mg==",
1068 | "name": "prettier-atom",
1069 | "description": "An atom package for the prettier formatter.",
1070 | "url": "https://github.com/prettier/prettier-atom",
1071 | "pushedAt": "2018-10-02T02:26:58Z",
1072 | "stargazers": {
1073 | "totalCount": 673
1074 | },
1075 | "forkCount": 88,
1076 | "languages": {
1077 | "edges": [
1078 | {
1079 | "node": {
1080 | "name": "JavaScript"
1081 | }
1082 | }
1083 | ]
1084 | }
1085 | }
1086 | },
1087 | {
1088 | "node": {
1089 | "id": "MDEwOlJlcG9zaXRvcnkxMjk5OTcyMzY=",
1090 | "name": "learn-react",
1091 | "description": "Learn React with a laser focused, guided approach.",
1092 | "url": "https://github.com/kentcdodds/learn-react",
1093 | "pushedAt": "2018-09-30T20:21:53Z",
1094 | "stargazers": {
1095 | "totalCount": 107
1096 | },
1097 | "forkCount": 34,
1098 | "languages": {
1099 | "edges": [
1100 | {
1101 | "node": {
1102 | "name": "JavaScript"
1103 | }
1104 | }
1105 | ]
1106 | }
1107 | }
1108 | },
1109 | {
1110 | "node": {
1111 | "id": "MDEwOlJlcG9zaXRvcnk3ODg5MzEwOA==",
1112 | "name": "prettier",
1113 | "description": "Prettier is an opinionated JavaScript formatter.",
1114 | "url": "https://github.com/kentcdodds/prettier",
1115 | "pushedAt": "2018-09-30T13:23:21Z",
1116 | "stargazers": {
1117 | "totalCount": 0
1118 | },
1119 | "forkCount": 1,
1120 | "languages": {
1121 | "edges": [
1122 | {
1123 | "node": {
1124 | "name": "JavaScript"
1125 | }
1126 | }
1127 | ]
1128 | }
1129 | }
1130 | },
1131 | {
1132 | "node": {
1133 | "id": "MDEwOlJlcG9zaXRvcnkxMjUzNjgyMTg=",
1134 | "name": "reactjs.org",
1135 | "description": "The React documentation website",
1136 | "url": "https://github.com/kentcdodds/reactjs.org",
1137 | "pushedAt": "2018-09-28T14:55:47Z",
1138 | "stargazers": {
1139 | "totalCount": 0
1140 | },
1141 | "forkCount": 1,
1142 | "languages": {
1143 | "edges": [
1144 | {
1145 | "node": {
1146 | "name": "JavaScript"
1147 | }
1148 | }
1149 | ]
1150 | }
1151 | }
1152 | },
1153 | {
1154 | "node": {
1155 | "id": "MDEwOlJlcG9zaXRvcnkxMzE5NDMzMjU=",
1156 | "name": "react-testing-library-course",
1157 | "description": "Test React with react-testing-library",
1158 | "url": "https://github.com/kentcdodds/react-testing-library-course",
1159 | "pushedAt": "2018-09-27T20:17:56Z",
1160 | "stargazers": {
1161 | "totalCount": 141
1162 | },
1163 | "forkCount": 17,
1164 | "languages": {
1165 | "edges": [
1166 | {
1167 | "node": {
1168 | "name": "JavaScript"
1169 | }
1170 | }
1171 | ]
1172 | }
1173 | }
1174 | },
1175 | {
1176 | "node": {
1177 | "id": "MDEwOlJlcG9zaXRvcnkxNTAzMTk3MTg=",
1178 | "name": "react-suspense-simple-example",
1179 | "description": null,
1180 | "url": "https://github.com/kentcdodds/react-suspense-simple-example",
1181 | "pushedAt": "2018-09-25T22:30:45Z",
1182 | "stargazers": {
1183 | "totalCount": 36
1184 | },
1185 | "forkCount": 0,
1186 | "languages": {
1187 | "edges": [
1188 | {
1189 | "node": {
1190 | "name": "HTML"
1191 | }
1192 | }
1193 | ]
1194 | }
1195 | }
1196 | },
1197 | {
1198 | "node": {
1199 | "id": "MDEwOlJlcG9zaXRvcnkxMjY0ODMxOTU=",
1200 | "name": "wait-for-expect",
1201 | "description": "Wait for expectation to be true, useful for integration and end to end testing. Integral part of react-testing-library.",
1202 | "url": "https://github.com/TheBrainFamily/wait-for-expect",
1203 | "pushedAt": "2018-09-25T13:54:10Z",
1204 | "stargazers": {
1205 | "totalCount": 72
1206 | },
1207 | "forkCount": 4,
1208 | "languages": {
1209 | "edges": [
1210 | {
1211 | "node": {
1212 | "name": "TypeScript"
1213 | }
1214 | }
1215 | ]
1216 | }
1217 | }
1218 | },
1219 | {
1220 | "node": {
1221 | "id": "MDEwOlJlcG9zaXRvcnkxMjM0ODg4OA==",
1222 | "name": "genie",
1223 | "description": "Keyboard control for web applications (better than cryptic shortcuts). 3.5K minified & gzipped",
1224 | "url": "https://github.com/kentcdodds/genie",
1225 | "pushedAt": "2018-09-25T13:14:14Z",
1226 | "stargazers": {
1227 | "totalCount": 444
1228 | },
1229 | "forkCount": 35,
1230 | "languages": {
1231 | "edges": [
1232 | {
1233 | "node": {
1234 | "name": "JavaScript"
1235 | }
1236 | }
1237 | ]
1238 | }
1239 | }
1240 | },
1241 | {
1242 | "node": {
1243 | "id": "MDEwOlJlcG9zaXRvcnkxNDk2NjkzMjA=",
1244 | "name": "docz",
1245 | "description": "✍🏻It has never been so easy to document your things!",
1246 | "url": "https://github.com/kentcdodds/docz",
1247 | "pushedAt": "2018-09-24T23:41:31Z",
1248 | "stargazers": {
1249 | "totalCount": 0
1250 | },
1251 | "forkCount": 0,
1252 | "languages": {
1253 | "edges": [
1254 | {
1255 | "node": {
1256 | "name": "TypeScript"
1257 | }
1258 | }
1259 | ]
1260 | }
1261 | }
1262 | },
1263 | {
1264 | "node": {
1265 | "id": "MDEwOlJlcG9zaXRvcnkxMzkyNzk2NzA=",
1266 | "name": "jest-watch-select-projects",
1267 | "description": "Select which Jest projects to run",
1268 | "url": "https://github.com/kentcdodds/jest-watch-select-projects",
1269 | "pushedAt": "2018-09-20T17:58:20Z",
1270 | "stargazers": {
1271 | "totalCount": 0
1272 | },
1273 | "forkCount": 0,
1274 | "languages": {
1275 | "edges": [
1276 | {
1277 | "node": {
1278 | "name": "JavaScript"
1279 | }
1280 | }
1281 | ]
1282 | }
1283 | }
1284 | },
1285 | {
1286 | "node": {
1287 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3OTIzMDc=",
1288 | "name": "confident-react-examples",
1289 | "description": null,
1290 | "url": "https://github.com/kentcdodds/confident-react-examples",
1291 | "pushedAt": "2018-09-20T16:58:21Z",
1292 | "stargazers": {
1293 | "totalCount": 10
1294 | },
1295 | "forkCount": 0,
1296 | "languages": {
1297 | "edges": [
1298 | {
1299 | "node": {
1300 | "name": "JavaScript"
1301 | }
1302 | }
1303 | ]
1304 | }
1305 | }
1306 | },
1307 | {
1308 | "node": {
1309 | "id": "MDEwOlJlcG9zaXRvcnk0MTQ4NDg5OQ==",
1310 | "name": "eslint-config-kentcdodds",
1311 | "description": "ESLint configuration for projects that I do... Feel free to use this!",
1312 | "url": "https://github.com/kentcdodds/eslint-config-kentcdodds",
1313 | "pushedAt": "2018-09-20T16:07:09Z",
1314 | "stargazers": {
1315 | "totalCount": 70
1316 | },
1317 | "forkCount": 20,
1318 | "languages": {
1319 | "edges": [
1320 | {
1321 | "node": {
1322 | "name": "JavaScript"
1323 | }
1324 | }
1325 | ]
1326 | }
1327 | }
1328 | },
1329 | {
1330 | "node": {
1331 | "id": "MDEwOlJlcG9zaXRvcnk5NzI3NjgzOQ==",
1332 | "name": "babel-plugin-macros-example",
1333 | "description": "An example of how you might make a babel-plugin-macros macro repo",
1334 | "url": "https://github.com/kentcdodds/babel-plugin-macros-example",
1335 | "pushedAt": "2018-09-16T22:39:39Z",
1336 | "stargazers": {
1337 | "totalCount": 18
1338 | },
1339 | "forkCount": 6,
1340 | "languages": {
1341 | "edges": [
1342 | {
1343 | "node": {
1344 | "name": "JavaScript"
1345 | }
1346 | }
1347 | ]
1348 | }
1349 | }
1350 | },
1351 | {
1352 | "node": {
1353 | "id": "MDEwOlJlcG9zaXRvcnkxMzYxMDc5Mjc=",
1354 | "name": "downshift-examples",
1355 | "description": "Created with CodeSandbox",
1356 | "url": "https://github.com/kentcdodds/downshift-examples",
1357 | "pushedAt": "2018-09-14T13:43:09Z",
1358 | "stargazers": {
1359 | "totalCount": 33
1360 | },
1361 | "forkCount": 8,
1362 | "languages": {
1363 | "edges": [
1364 | {
1365 | "node": {
1366 | "name": "HTML"
1367 | }
1368 | }
1369 | ]
1370 | }
1371 | }
1372 | },
1373 | {
1374 | "node": {
1375 | "id": "MDEwOlJlcG9zaXRvcnkxMDc1OTg0MDE=",
1376 | "name": "the-beginner-s-guide-to-reactjs",
1377 | "description": "The beginner's Guide to ReactJS course material for Egghead.io",
1378 | "url": "https://github.com/eggheadio-projects/the-beginner-s-guide-to-reactjs",
1379 | "pushedAt": "2018-09-12T10:00:23Z",
1380 | "stargazers": {
1381 | "totalCount": 135
1382 | },
1383 | "forkCount": 64,
1384 | "languages": {
1385 | "edges": [
1386 | {
1387 | "node": {
1388 | "name": "HTML"
1389 | }
1390 | }
1391 | ]
1392 | }
1393 | }
1394 | },
1395 | {
1396 | "node": {
1397 | "id": "MDEwOlJlcG9zaXRvcnk5NjI4Mjc1MQ==",
1398 | "name": "babel-plugin-preval",
1399 | "description": "🐣 Pre-evaluate code at build-time",
1400 | "url": "https://github.com/kentcdodds/babel-plugin-preval",
1401 | "pushedAt": "2018-09-12T08:56:13Z",
1402 | "stargazers": {
1403 | "totalCount": 781
1404 | },
1405 | "forkCount": 50,
1406 | "languages": {
1407 | "edges": [
1408 | {
1409 | "node": {
1410 | "name": "JavaScript"
1411 | }
1412 | }
1413 | ]
1414 | }
1415 | }
1416 | },
1417 | {
1418 | "node": {
1419 | "id": "MDEwOlJlcG9zaXRvcnkzODMyMTk4NQ==",
1420 | "name": "cloc",
1421 | "description": "An npm module for distributing cloc by Al Danial",
1422 | "url": "https://github.com/kentcdodds/cloc",
1423 | "pushedAt": "2018-09-11T01:02:43Z",
1424 | "stargazers": {
1425 | "totalCount": 80
1426 | },
1427 | "forkCount": 8,
1428 | "languages": {
1429 | "edges": [
1430 | {
1431 | "node": {
1432 | "name": "Perl"
1433 | }
1434 | }
1435 | ]
1436 | }
1437 | }
1438 | },
1439 | {
1440 | "node": {
1441 | "id": "MDEwOlJlcG9zaXRvcnkxMTQzMTg5MTQ=",
1442 | "name": "typing-for-kids",
1443 | "description": "A little app I made for my kids for Christmas :)",
1444 | "url": "https://github.com/kentcdodds/typing-for-kids",
1445 | "pushedAt": "2018-09-08T16:44:41Z",
1446 | "stargazers": {
1447 | "totalCount": 26
1448 | },
1449 | "forkCount": 5,
1450 | "languages": {
1451 | "edges": [
1452 | {
1453 | "node": {
1454 | "name": "HTML"
1455 | }
1456 | }
1457 | ]
1458 | }
1459 | }
1460 | },
1461 | {
1462 | "node": {
1463 | "id": "MDEwOlJlcG9zaXRvcnkzNDI5NTc1NA==",
1464 | "name": "es6-workshop",
1465 | "description": "A very hands on :open_hands: workshop :computer: about ES6 and beyond.",
1466 | "url": "https://github.com/kentcdodds/es6-workshop",
1467 | "pushedAt": "2018-09-05T15:04:05Z",
1468 | "stargazers": {
1469 | "totalCount": 256
1470 | },
1471 | "forkCount": 78,
1472 | "languages": {
1473 | "edges": [
1474 | {
1475 | "node": {
1476 | "name": "JavaScript"
1477 | }
1478 | }
1479 | ]
1480 | }
1481 | }
1482 | },
1483 | {
1484 | "node": {
1485 | "id": "MDEwOlJlcG9zaXRvcnkxNDcwNDgyODk=",
1486 | "name": "jest-axe",
1487 | "description": "Custom Jest matcher for aXe for testing accessibility ♿️🃏",
1488 | "url": "https://github.com/kentcdodds/jest-axe",
1489 | "pushedAt": "2018-09-04T16:53:27Z",
1490 | "stargazers": {
1491 | "totalCount": 1
1492 | },
1493 | "forkCount": 0,
1494 | "languages": {
1495 | "edges": [
1496 | {
1497 | "node": {
1498 | "name": "JavaScript"
1499 | }
1500 | }
1501 | ]
1502 | }
1503 | }
1504 | },
1505 | {
1506 | "node": {
1507 | "id": "MDEwOlJlcG9zaXRvcnkxNDY5MTkwMDg=",
1508 | "name": "babel-runtime-example",
1509 | "description": "An example of how to use @babel/plugin-transform-runtime",
1510 | "url": "https://github.com/kentcdodds/babel-runtime-example",
1511 | "pushedAt": "2018-08-31T16:48:42Z",
1512 | "stargazers": {
1513 | "totalCount": 15
1514 | },
1515 | "forkCount": 1,
1516 | "languages": {
1517 | "edges": [
1518 | {
1519 | "node": {
1520 | "name": "JavaScript"
1521 | }
1522 | }
1523 | ]
1524 | }
1525 | }
1526 | },
1527 | {
1528 | "node": {
1529 | "id": "MDEwOlJlcG9zaXRvcnk2NjUyMzQ0MA==",
1530 | "name": "match-sorter",
1531 | "description": "Simple, expected, and deterministic best-match sorting of an array in JavaScript",
1532 | "url": "https://github.com/kentcdodds/match-sorter",
1533 | "pushedAt": "2018-08-29T23:44:21Z",
1534 | "stargazers": {
1535 | "totalCount": 1019
1536 | },
1537 | "forkCount": 44,
1538 | "languages": {
1539 | "edges": [
1540 | {
1541 | "node": {
1542 | "name": "JavaScript"
1543 | }
1544 | }
1545 | ]
1546 | }
1547 | }
1548 | },
1549 | {
1550 | "node": {
1551 | "id": "MDEwOlJlcG9zaXRvcnk1NTQ3MDcxMw==",
1552 | "name": "refined-github",
1553 | "description": "Chrome extension that simplifies the GitHub interface and adds useful features",
1554 | "url": "https://github.com/kentcdodds/refined-github",
1555 | "pushedAt": "2018-08-28T19:08:48Z",
1556 | "stargazers": {
1557 | "totalCount": 1
1558 | },
1559 | "forkCount": 0,
1560 | "languages": {
1561 | "edges": [
1562 | {
1563 | "node": {
1564 | "name": "CSS"
1565 | }
1566 | }
1567 | ]
1568 | }
1569 | }
1570 | },
1571 | {
1572 | "node": {
1573 | "id": "MDEwOlJlcG9zaXRvcnk2MzYzNTgwMg==",
1574 | "name": "webpack-config-utils",
1575 | "description": "Utilities to help your webpack config be easier to read",
1576 | "url": "https://github.com/kentcdodds/webpack-config-utils",
1577 | "pushedAt": "2018-08-28T17:12:45Z",
1578 | "stargazers": {
1579 | "totalCount": 212
1580 | },
1581 | "forkCount": 19,
1582 | "languages": {
1583 | "edges": [
1584 | {
1585 | "node": {
1586 | "name": "JavaScript"
1587 | }
1588 | }
1589 | ]
1590 | }
1591 | }
1592 | },
1593 | {
1594 | "node": {
1595 | "id": "MDEwOlJlcG9zaXRvcnkyNTkyNzYxOQ==",
1596 | "name": "dotfiles",
1597 | "description": null,
1598 | "url": "https://github.com/kentcdodds/dotfiles",
1599 | "pushedAt": "2018-08-23T22:03:09Z",
1600 | "stargazers": {
1601 | "totalCount": 55
1602 | },
1603 | "forkCount": 13,
1604 | "languages": {
1605 | "edges": [
1606 | {
1607 | "node": {
1608 | "name": "Shell"
1609 | }
1610 | }
1611 | ]
1612 | }
1613 | }
1614 | },
1615 | {
1616 | "node": {
1617 | "id": "MDEwOlJlcG9zaXRvcnkxMzYwODEzMTk=",
1618 | "name": "react-testing-library-examples",
1619 | "description": "Created with CodeSandbox",
1620 | "url": "https://github.com/kentcdodds/react-testing-library-examples",
1621 | "pushedAt": "2018-08-23T03:55:52Z",
1622 | "stargazers": {
1623 | "totalCount": 30
1624 | },
1625 | "forkCount": 2,
1626 | "languages": {
1627 | "edges": [
1628 | {
1629 | "node": {
1630 | "name": "HTML"
1631 | }
1632 | }
1633 | ]
1634 | }
1635 | }
1636 | },
1637 | {
1638 | "node": {
1639 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3NzM0OTQ=",
1640 | "name": "rtl-manual-mock-ex",
1641 | "description": null,
1642 | "url": "https://github.com/kentcdodds/rtl-manual-mock-ex",
1643 | "pushedAt": "2018-08-22T23:26:34Z",
1644 | "stargazers": {
1645 | "totalCount": 0
1646 | },
1647 | "forkCount": 0,
1648 | "languages": {
1649 | "edges": [
1650 | {
1651 | "node": {
1652 | "name": "JavaScript"
1653 | }
1654 | }
1655 | ]
1656 | }
1657 | }
1658 | },
1659 | {
1660 | "node": {
1661 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3MzMwMTc=",
1662 | "name": "node-travis-ci",
1663 | "description": "node library to access the Travis-CI API",
1664 | "url": "https://github.com/kentcdodds/node-travis-ci",
1665 | "pushedAt": "2018-08-22T16:11:03Z",
1666 | "stargazers": {
1667 | "totalCount": 0
1668 | },
1669 | "forkCount": 0,
1670 | "languages": {
1671 | "edges": [
1672 | {
1673 | "node": {
1674 | "name": "JavaScript"
1675 | }
1676 | }
1677 | ]
1678 | }
1679 | }
1680 | },
1681 | {
1682 | "node": {
1683 | "id": "MDEwOlJlcG9zaXRvcnkxNDU0NDg0NjE=",
1684 | "name": "from-html",
1685 | "description": "Get element references directly from a HTML string",
1686 | "url": "https://github.com/kentcdodds/from-html",
1687 | "pushedAt": "2018-08-20T17:20:14Z",
1688 | "stargazers": {
1689 | "totalCount": 0
1690 | },
1691 | "forkCount": 0,
1692 | "languages": {
1693 | "edges": [
1694 | {
1695 | "node": {
1696 | "name": "JavaScript"
1697 | }
1698 | }
1699 | ]
1700 | }
1701 | }
1702 | },
1703 | {
1704 | "node": {
1705 | "id": "MDEwOlJlcG9zaXRvcnk4MzY0ODk1NQ==",
1706 | "name": "nps-utils",
1707 | "description": "Utilities for http://npm.im/nps (npm-package-scripts)",
1708 | "url": "https://github.com/kentcdodds/nps-utils",
1709 | "pushedAt": "2018-08-12T17:31:57Z",
1710 | "stargazers": {
1711 | "totalCount": 59
1712 | },
1713 | "forkCount": 9,
1714 | "languages": {
1715 | "edges": [
1716 | {
1717 | "node": {
1718 | "name": "JavaScript"
1719 | }
1720 | }
1721 | ]
1722 | }
1723 | }
1724 | },
1725 | {
1726 | "node": {
1727 | "id": "MDEwOlJlcG9zaXRvcnkxMDAyOTgxMTk=",
1728 | "name": "codegen.macro",
1729 | "description": null,
1730 | "url": "https://github.com/kentcdodds/codegen.macro",
1731 | "pushedAt": "2018-08-08T16:32:38Z",
1732 | "stargazers": {
1733 | "totalCount": 9
1734 | },
1735 | "forkCount": 2,
1736 | "languages": {
1737 | "edges": [
1738 | {
1739 | "node": {
1740 | "name": "JavaScript"
1741 | }
1742 | }
1743 | ]
1744 | }
1745 | }
1746 | },
1747 | {
1748 | "node": {
1749 | "id": "MDEwOlJlcG9zaXRvcnkxNDM3NDk2NTc=",
1750 | "name": "mdx",
1751 | "description": "JSX in Markdown for ambitious projects",
1752 | "url": "https://github.com/kentcdodds/mdx",
1753 | "pushedAt": "2018-08-06T15:53:02Z",
1754 | "stargazers": {
1755 | "totalCount": 0
1756 | },
1757 | "forkCount": 0,
1758 | "languages": {
1759 | "edges": [
1760 | {
1761 | "node": {
1762 | "name": "JavaScript"
1763 | }
1764 | }
1765 | ]
1766 | }
1767 | }
1768 | },
1769 | {
1770 | "node": {
1771 | "id": "MDEwOlJlcG9zaXRvcnkxMTE4MzQzNDk=",
1772 | "name": "kit",
1773 | "description": "Tools for developing, documenting, and testing React component libraries",
1774 | "url": "https://github.com/c8r/kit",
1775 | "pushedAt": "2018-08-04T23:25:21Z",
1776 | "stargazers": {
1777 | "totalCount": 1052
1778 | },
1779 | "forkCount": 28,
1780 | "languages": {
1781 | "edges": [
1782 | {
1783 | "node": {
1784 | "name": "JavaScript"
1785 | }
1786 | }
1787 | ]
1788 | }
1789 | }
1790 | },
1791 | {
1792 | "node": {
1793 | "id": "MDEwOlJlcG9zaXRvcnkxNDIyMjEyNzQ=",
1794 | "name": "graphql-request-1.7.0-bug",
1795 | "description": null,
1796 | "url": "https://github.com/kentcdodds/graphql-request-1.7.0-bug",
1797 | "pushedAt": "2018-08-03T17:36:18Z",
1798 | "stargazers": {
1799 | "totalCount": 0
1800 | },
1801 | "forkCount": 0,
1802 | "languages": {
1803 | "edges": [
1804 | {
1805 | "node": {
1806 | "name": "HTML"
1807 | }
1808 | }
1809 | ]
1810 | }
1811 | }
1812 | },
1813 | {
1814 | "node": {
1815 | "id": "MDEwOlJlcG9zaXRvcnkxNDI1OTg4NTE=",
1816 | "name": "codesandbox-client",
1817 | "description": "An online code editor tailored for web application development 🏖️",
1818 | "url": "https://github.com/kentcdodds/codesandbox-client",
1819 | "pushedAt": "2018-07-27T17:45:54Z",
1820 | "stargazers": {
1821 | "totalCount": 0
1822 | },
1823 | "forkCount": 0,
1824 | "languages": {
1825 | "edges": [
1826 | {
1827 | "node": {
1828 | "name": "Dockerfile"
1829 | }
1830 | }
1831 | ]
1832 | }
1833 | }
1834 | },
1835 | {
1836 | "node": {
1837 | "id": "MDEwOlJlcG9zaXRvcnkxMDI4NzYwMjM=",
1838 | "name": "emotion",
1839 | "description": "⚡️ The Next Generation of CSS-in-JS",
1840 | "url": "https://github.com/kentcdodds/emotion",
1841 | "pushedAt": "2018-07-26T05:36:34Z",
1842 | "stargazers": {
1843 | "totalCount": 1
1844 | },
1845 | "forkCount": 0,
1846 | "languages": {
1847 | "edges": [
1848 | {
1849 | "node": {
1850 | "name": "JavaScript"
1851 | }
1852 | }
1853 | ]
1854 | }
1855 | }
1856 | },
1857 | {
1858 | "node": {
1859 | "id": "MDEwOlJlcG9zaXRvcnkxNDE2MzUwMjY=",
1860 | "name": "webpack-serve",
1861 | "description": "A lean, modern, and flexible webpack development server",
1862 | "url": "https://github.com/kentcdodds/webpack-serve",
1863 | "pushedAt": "2018-07-19T22:02:13Z",
1864 | "stargazers": {
1865 | "totalCount": 0
1866 | },
1867 | "forkCount": 0,
1868 | "languages": {
1869 | "edges": [
1870 | {
1871 | "node": {
1872 | "name": "JavaScript"
1873 | }
1874 | }
1875 | ]
1876 | }
1877 | }
1878 | },
1879 | {
1880 | "node": {
1881 | "id": "MDEwOlJlcG9zaXRvcnkxNDAzMTA4ODU=",
1882 | "name": "shallow-to-mock",
1883 | "description": null,
1884 | "url": "https://github.com/kentcdodds/shallow-to-mock",
1885 | "pushedAt": "2018-07-09T16:14:19Z",
1886 | "stargazers": {
1887 | "totalCount": 13
1888 | },
1889 | "forkCount": 0,
1890 | "languages": {
1891 | "edges": [
1892 | {
1893 | "node": {
1894 | "name": "JavaScript"
1895 | }
1896 | }
1897 | ]
1898 | }
1899 | }
1900 | },
1901 | {
1902 | "node": {
1903 | "id": "MDEwOlJlcG9zaXRvcnk4NjEwMDIwNg==",
1904 | "name": "jest-glamor-react",
1905 | "description": "Jest utilities for Glamor and React",
1906 | "url": "https://github.com/kentcdodds/jest-glamor-react",
1907 | "pushedAt": "2018-07-03T20:37:01Z",
1908 | "stargazers": {
1909 | "totalCount": 85
1910 | },
1911 | "forkCount": 22,
1912 | "languages": {
1913 | "edges": [
1914 | {
1915 | "node": {
1916 | "name": "JavaScript"
1917 | }
1918 | }
1919 | ]
1920 | }
1921 | }
1922 | },
1923 | {
1924 | "node": {
1925 | "id": "MDEwOlJlcG9zaXRvcnk4MDY0NDI4Ng==",
1926 | "name": "eslint-plugin-react",
1927 | "description": "React specific linting rules for ESLint",
1928 | "url": "https://github.com/kentcdodds/eslint-plugin-react",
1929 | "pushedAt": "2018-07-03T00:48:35Z",
1930 | "stargazers": {
1931 | "totalCount": 0
1932 | },
1933 | "forkCount": 0,
1934 | "languages": {
1935 | "edges": [
1936 | {
1937 | "node": {
1938 | "name": "JavaScript"
1939 | }
1940 | }
1941 | ]
1942 | }
1943 | }
1944 | },
1945 | {
1946 | "node": {
1947 | "id": "MDEwOlJlcG9zaXRvcnkzODU4MzM4NA==",
1948 | "name": "ama",
1949 | "description": "Ask me anything!",
1950 | "url": "https://github.com/kentcdodds/ama",
1951 | "pushedAt": "2018-07-02T00:22:46Z",
1952 | "stargazers": {
1953 | "totalCount": 347
1954 | },
1955 | "forkCount": 33,
1956 | "languages": {
1957 | "edges": []
1958 | }
1959 | }
1960 | },
1961 | {
1962 | "node": {
1963 | "id": "MDEwOlJlcG9zaXRvcnk5NzI1MjA1MA==",
1964 | "name": "preval.macro",
1965 | "description": "Pre-evaluate code at build-time with babel-macros",
1966 | "url": "https://github.com/kentcdodds/preval.macro",
1967 | "pushedAt": "2018-06-27T22:10:41Z",
1968 | "stargazers": {
1969 | "totalCount": 29
1970 | },
1971 | "forkCount": 2,
1972 | "languages": {
1973 | "edges": [
1974 | {
1975 | "node": {
1976 | "name": "JavaScript"
1977 | }
1978 | }
1979 | ]
1980 | }
1981 | }
1982 | },
1983 | {
1984 | "node": {
1985 | "id": "MDEwOlJlcG9zaXRvcnkxMzg5MzY4MzQ=",
1986 | "name": "npm-1",
1987 | "description": ":ship: Set of semantic-release plugins to publish to a npm registry",
1988 | "url": "https://github.com/kentcdodds/npm-1",
1989 | "pushedAt": "2018-06-27T22:06:37Z",
1990 | "stargazers": {
1991 | "totalCount": 0
1992 | },
1993 | "forkCount": 0,
1994 | "languages": {
1995 | "edges": [
1996 | {
1997 | "node": {
1998 | "name": "JavaScript"
1999 | }
2000 | }
2001 | ]
2002 | }
2003 | }
2004 | },
2005 | {
2006 | "node": {
2007 | "id": "MDEwOlJlcG9zaXRvcnkxMDAxNjAwMTQ=",
2008 | "name": "babel-plugin-codegen",
2009 | "description": "💥 Generate code at build-time",
2010 | "url": "https://github.com/kentcdodds/babel-plugin-codegen",
2011 | "pushedAt": "2018-06-27T21:21:28Z",
2012 | "stargazers": {
2013 | "totalCount": 150
2014 | },
2015 | "forkCount": 12,
2016 | "languages": {
2017 | "edges": [
2018 | {
2019 | "node": {
2020 | "name": "JavaScript"
2021 | }
2022 | }
2023 | ]
2024 | }
2025 | }
2026 | },
2027 | {
2028 | "node": {
2029 | "id": "MDEwOlJlcG9zaXRvcnk0MDI0MjA1MQ==",
2030 | "name": "starwars-names",
2031 | "description": "Get a random Star Wars name",
2032 | "url": "https://github.com/kentcdodds/starwars-names",
2033 | "pushedAt": "2018-06-26T23:08:03Z",
2034 | "stargazers": {
2035 | "totalCount": 132
2036 | },
2037 | "forkCount": 139,
2038 | "languages": {
2039 | "edges": [
2040 | {
2041 | "node": {
2042 | "name": "JavaScript"
2043 | }
2044 | }
2045 | ]
2046 | }
2047 | }
2048 | },
2049 | {
2050 | "node": {
2051 | "id": "MDEwOlJlcG9zaXRvcnkxMzg3NzE2NzI=",
2052 | "name": "create-react-app-react-testing-library-example",
2053 | "description": null,
2054 | "url": "https://github.com/kentcdodds/create-react-app-react-testing-library-example",
2055 | "pushedAt": "2018-06-26T17:35:07Z",
2056 | "stargazers": {
2057 | "totalCount": 11
2058 | },
2059 | "forkCount": 1,
2060 | "languages": {
2061 | "edges": [
2062 | {
2063 | "node": {
2064 | "name": "HTML"
2065 | }
2066 | }
2067 | ]
2068 | }
2069 | }
2070 | },
2071 | {
2072 | "node": {
2073 | "id": "MDEwOlJlcG9zaXRvcnk4NDI2MDQ0MQ==",
2074 | "name": "create-react-app",
2075 | "description": "Create React apps with no build configuration.",
2076 | "url": "https://github.com/kentcdodds/create-react-app",
2077 | "pushedAt": "2018-06-26T16:46:12Z",
2078 | "stargazers": {
2079 | "totalCount": 0
2080 | },
2081 | "forkCount": 1,
2082 | "languages": {
2083 | "edges": [
2084 | {
2085 | "node": {
2086 | "name": "JavaScript"
2087 | }
2088 | }
2089 | ]
2090 | }
2091 | }
2092 | },
2093 | {
2094 | "node": {
2095 | "id": "MDEwOlJlcG9zaXRvcnk1NDEzNjMwNg==",
2096 | "name": "eslint-find-rules",
2097 | "description": "Find built-in ESLint rules you don't have in your custom config",
2098 | "url": "https://github.com/sarbbottam/eslint-find-rules",
2099 | "pushedAt": "2018-06-25T20:26:41Z",
2100 | "stargazers": {
2101 | "totalCount": 100
2102 | },
2103 | "forkCount": 15,
2104 | "languages": {
2105 | "edges": [
2106 | {
2107 | "node": {
2108 | "name": "JavaScript"
2109 | }
2110 | }
2111 | ]
2112 | }
2113 | }
2114 | },
2115 | {
2116 | "node": {
2117 | "id": "MDEwOlJlcG9zaXRvcnkxMzg2MjkxMzU=",
2118 | "name": "never-use-shallow-rendering",
2119 | "description": null,
2120 | "url": "https://github.com/kentcdodds/never-use-shallow-rendering",
2121 | "pushedAt": "2018-06-25T17:38:42Z",
2122 | "stargazers": {
2123 | "totalCount": 14
2124 | },
2125 | "forkCount": 0,
2126 | "languages": {
2127 | "edges": [
2128 | {
2129 | "node": {
2130 | "name": "JavaScript"
2131 | }
2132 | }
2133 | ]
2134 | }
2135 | }
2136 | },
2137 | {
2138 | "node": {
2139 | "id": "MDEwOlJlcG9zaXRvcnkxMzc5NzUwMTI=",
2140 | "name": "js-mocking-fundamentals",
2141 | "description": null,
2142 | "url": "https://github.com/kentcdodds/js-mocking-fundamentals",
2143 | "pushedAt": "2018-06-23T03:03:58Z",
2144 | "stargazers": {
2145 | "totalCount": 37
2146 | },
2147 | "forkCount": 3,
2148 | "languages": {
2149 | "edges": [
2150 | {
2151 | "node": {
2152 | "name": "JavaScript"
2153 | }
2154 | }
2155 | ]
2156 | }
2157 | }
2158 | },
2159 | {
2160 | "node": {
2161 | "id": "MDEwOlJlcG9zaXRvcnkzMDQ0NjYzNg==",
2162 | "name": "api-check",
2163 | "description": "VanillaJS version of ReactJS propTypes",
2164 | "url": "https://github.com/kentcdodds/api-check",
2165 | "pushedAt": "2018-06-18T19:36:24Z",
2166 | "stargazers": {
2167 | "totalCount": 193
2168 | },
2169 | "forkCount": 26,
2170 | "languages": {
2171 | "edges": [
2172 | {
2173 | "node": {
2174 | "name": "HTML"
2175 | }
2176 | }
2177 | ]
2178 | }
2179 | }
2180 | },
2181 | {
2182 | "node": {
2183 | "id": "MDEwOlJlcG9zaXRvcnkxMzc3ODcxNDQ=",
2184 | "name": "react-test-isolation",
2185 | "description": null,
2186 | "url": "https://github.com/kentcdodds/react-test-isolation",
2187 | "pushedAt": "2018-06-18T18:02:40Z",
2188 | "stargazers": {
2189 | "totalCount": 9
2190 | },
2191 | "forkCount": 0,
2192 | "languages": {
2193 | "edges": [
2194 | {
2195 | "node": {
2196 | "name": "HTML"
2197 | }
2198 | }
2199 | ]
2200 | }
2201 | }
2202 | }
2203 | ]
2204 | },
2205 | "organizations": {
2206 | "edges": [
2207 | {
2208 | "node": {
2209 | "avatarUrl": "https://avatars3.githubusercontent.com/u/139426?v=4",
2210 | "id": "MDEyOk9yZ2FuaXphdGlvbjEzOTQyNg==",
2211 | "login": "angular"
2212 | }
2213 | },
2214 | {
2215 | "node": {
2216 | "avatarUrl": "https://avatars1.githubusercontent.com/u/476675?v=4",
2217 | "id": "MDEyOk9yZ2FuaXphdGlvbjQ3NjY3NQ==",
2218 | "login": "paypal"
2219 | }
2220 | },
2221 | {
2222 | "node": {
2223 | "avatarUrl": "https://avatars2.githubusercontent.com/u/1725583?v=4",
2224 | "id": "MDEyOk9yZ2FuaXphdGlvbjE3MjU1ODM=",
2225 | "login": "tc39"
2226 | }
2227 | },
2228 | {
2229 | "node": {
2230 | "avatarUrl": "https://avatars2.githubusercontent.com/u/2105791?v=4",
2231 | "id": "MDEyOk9yZ2FuaXphdGlvbjIxMDU3OTE=",
2232 | "login": "webpack"
2233 | }
2234 | },
2235 | {
2236 | "node": {
2237 | "avatarUrl": "https://avatars2.githubusercontent.com/u/5658226?v=4",
2238 | "id": "MDEyOk9yZ2FuaXphdGlvbjU2NTgyMjY=",
2239 | "login": "expressjs"
2240 | }
2241 | },
2242 | {
2243 | "node": {
2244 | "avatarUrl": "https://avatars3.githubusercontent.com/u/6627683?v=4",
2245 | "id": "MDEyOk9yZ2FuaXphdGlvbjY2Mjc2ODM=",
2246 | "login": "gdgutah"
2247 | }
2248 | },
2249 | {
2250 | "node": {
2251 | "avatarUrl": "https://avatars3.githubusercontent.com/u/7704921?v=4",
2252 | "id": "MDEyOk9yZ2FuaXphdGlvbjc3MDQ5MjE=",
2253 | "login": "github-beta"
2254 | }
2255 | },
2256 | {
2257 | "node": {
2258 | "avatarUrl": "https://avatars2.githubusercontent.com/u/8039323?v=4",
2259 | "id": "MDEyOk9yZ2FuaXphdGlvbjgwMzkzMjM=",
2260 | "login": "airpair"
2261 | }
2262 | },
2263 | {
2264 | "node": {
2265 | "avatarUrl": "https://avatars2.githubusercontent.com/u/8120632?v=4",
2266 | "id": "MDEyOk9yZ2FuaXphdGlvbjgxMjA2MzI=",
2267 | "login": "SLC-JS-Learners"
2268 | }
2269 | },
2270 | {
2271 | "node": {
2272 | "avatarUrl": "https://avatars3.githubusercontent.com/u/8561755?v=4",
2273 | "id": "MDEyOk9yZ2FuaXphdGlvbjg1NjE3NTU=",
2274 | "login": "formly-js"
2275 | }
2276 | },
2277 | {
2278 | "node": {
2279 | "avatarUrl": "https://avatars1.githubusercontent.com/u/9486604?v=4",
2280 | "id": "MDEyOk9yZ2FuaXphdGlvbjk0ODY2MDQ=",
2281 | "login": "AngularAir"
2282 | }
2283 | },
2284 | {
2285 | "node": {
2286 | "avatarUrl": "https://avatars1.githubusercontent.com/u/9637642?v=4",
2287 | "id": "MDEyOk9yZ2FuaXphdGlvbjk2Mzc2NDI=",
2288 | "login": "babel"
2289 | }
2290 | },
2291 | {
2292 | "node": {
2293 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11040183?v=4",
2294 | "id": "MDEyOk9yZ2FuaXphdGlvbjExMDQwMTgz",
2295 | "login": "ReactWeek"
2296 | }
2297 | },
2298 | {
2299 | "node": {
2300 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11279140?v=4",
2301 | "id": "MDEyOk9yZ2FuaXphdGlvbjExMjc5MTQw",
2302 | "login": "forms-js"
2303 | }
2304 | },
2305 | {
2306 | "node": {
2307 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11602678?v=4",
2308 | "id": "MDEyOk9yZ2FuaXphdGlvbjExNjAyNjc4",
2309 | "login": "AngularClass"
2310 | }
2311 | },
2312 | {
2313 | "node": {
2314 | "avatarUrl": "https://avatars0.githubusercontent.com/u/11980392?v=4",
2315 | "id": "MDEyOk9yZ2FuaXphdGlvbjExOTgwMzky",
2316 | "login": "commitizen"
2317 | }
2318 | },
2319 | {
2320 | "node": {
2321 | "avatarUrl": "https://avatars2.githubusercontent.com/u/13523395?v=4",
2322 | "id": "MDEyOk9yZ2FuaXphdGlvbjEzNTIzMzk1",
2323 | "login": "istanbuljs"
2324 | }
2325 | },
2326 | {
2327 | "node": {
2328 | "avatarUrl": "https://avatars3.githubusercontent.com/u/14825423?v=4",
2329 | "id": "MDEyOk9yZ2FuaXphdGlvbjE0ODI1NDIz",
2330 | "login": "angular-todo-mvc"
2331 | }
2332 | },
2333 | {
2334 | "node": {
2335 | "avatarUrl": "https://avatars2.githubusercontent.com/u/15644605?v=4",
2336 | "id": "MDEyOk9yZ2FuaXphdGlvbjE1NjQ0NjA1",
2337 | "login": "eggheadio-github"
2338 | }
2339 | },
2340 | {
2341 | "node": {
2342 | "avatarUrl": "https://avatars3.githubusercontent.com/u/15834066?v=4",
2343 | "id": "MDEyOk9yZ2FuaXphdGlvbjE1ODM0MDY2",
2344 | "login": "javascriptair"
2345 | }
2346 | },
2347 | {
2348 | "node": {
2349 | "avatarUrl": "https://avatars3.githubusercontent.com/u/16443515?v=4",
2350 | "id": "MDEyOk9yZ2FuaXphdGlvbjE2NDQzNTE1",
2351 | "login": "conventional-changelog"
2352 | }
2353 | },
2354 | {
2355 | "node": {
2356 | "avatarUrl": "https://avatars2.githubusercontent.com/u/20658825?v=4",
2357 | "id": "MDEyOk9yZ2FuaXphdGlvbjIwNjU4ODI1",
2358 | "login": "styled-components"
2359 | }
2360 | },
2361 | {
2362 | "node": {
2363 | "avatarUrl": "https://avatars0.githubusercontent.com/u/25333538?v=4",
2364 | "id": "MDEyOk9yZ2FuaXphdGlvbjI1MzMzNTM4",
2365 | "login": "maintainers"
2366 | }
2367 | },
2368 | {
2369 | "node": {
2370 | "avatarUrl": "https://avatars2.githubusercontent.com/u/25822731?v=4",
2371 | "id": "MDEyOk9yZ2FuaXphdGlvbjI1ODIyNzMx",
2372 | "login": "prettier"
2373 | }
2374 | },
2375 | {
2376 | "node": {
2377 | "avatarUrl": "https://avatars3.githubusercontent.com/u/28913597?v=4",
2378 | "id": "MDEyOk9yZ2FuaXphdGlvbjI4OTEzNTk3",
2379 | "login": "babel-utils"
2380 | }
2381 | }
2382 | ]
2383 | }
2384 | }
2385 | }
2386 |
--------------------------------------------------------------------------------