Skip to content

feat: introduce data fetching via Relay / GraphQL #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions generators/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,12 @@ export = class extends Generator {
'.vscode',
'.editorconfig',
'_gitignore',
'.gqlconfig',
'.markdownlint.json',
'.prettierignore',
'.travis.yml',
'.watchmanconfig',
'codegen.yml',
'config',
'tsconfig.json',
].forEach(filename => {
Expand All @@ -96,25 +100,30 @@ export = class extends Generator {
}

public writing() {
;['public', 'src', 'package.json', 'README.md'].forEach(filename => {
this.fs.copyTpl(
this.templatePath(filename),
this.destinationPath(filename),
this.answers,
)
})
;['public', 'server', 'src', 'package.json', 'README.md'].forEach(
filename => {
this.fs.copyTpl(
this.templatePath(filename),
this.destinationPath(filename),
this.answers,
)
},
)
}

public install() {
this.npmInstall(
[
'@queso/kebab-case@^1',
'babel-plugin-relay@4',
'graphql-tag@2',
'immutability-helper@^3',
'react@^16',
'react-dom@^16',
'react-helmet@^5',
'react-intl@^2',
'react-redux@^7',
'react-relay@4',
'react-router-dom@^5',
'redux@^4',
'redux-thunk@^2',
Expand All @@ -126,39 +135,54 @@ export = class extends Generator {
this.npmInstall(
[
'@craco/craco@5',
'@graphql-codegen/cli@1',
'@graphql-codegen/introspection@1',
'@graphql-codegen/typescript@1',
'@graphql-codegen/typescript-graphql-files-modules@1',
'@graphql-codegen/typescript-operations@1',
'@graphql-codegen/typescript-resolvers@1',
'@jedmao/tsconfig',
'@playlyfe/gql@2',
'@testing-library/react@8',
'@types/fetch-mock@^7',
'@types/graphql@14',
'@types/jest@^24',
'@types/node@^12',
'@types/react@^16',
'@types/react-dom@^16',
'@types/react-helmet@^5',
'@types/react-intl@^2',
'@types/react-redux@^7',
'@types/react-relay@1',
'@types/react-router-dom@^4',
'@types/redux-logger@^3',
'@types/redux-mock-store@^1',
'@types/webpack-env@^1',
// TODO: https://github.com/callstack/linaria/issues/420
'core-js@2',
'concurrently@4',
'core-js@2', // TODO: https://github.com/callstack/linaria/issues/420
'fetch-mock@^7',
'graphql@14',
'husky@^2',
'jest-fetch-mock@^2',
'linaria@1',
'lint-staged@^8',
'prettier@^1',
'react-scripts@^3',
'react-testing-library@^7',
'redux-devtools-extension@^2',
'redux-logger@^3',
'redux-mock-store@^1',
'relay-compiler@4',
'relay-compiler-language-typescript@4',
'relay-test-utils@4',
'rimraf@^2',
'ts-essentials@^2',
'ts-helpers@1',
'typescript@^3',
],
{
'save-dev': true,
},
)
this.spawnCommandSync('npm', ['run', 'postinstall'])
}
}
4 changes: 4 additions & 0 deletions generators/app/templates/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ trim_trailing_whitespace = false
[{*.json,*.yml}]
indent_style = space
indent_size = 2

[src/__generated__/**/*.ts]
indent_style = space
indent_size = 4
5 changes: 5 additions & 0 deletions generators/app/templates/.gqlconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
schema: {
files: 'server/src/schemas/**/*.gql'
}
}
1 change: 1 addition & 0 deletions generators/app/templates/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/__generated__
2 changes: 1 addition & 1 deletion generators/app/templates/.travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ branches:

script:
- npm run check-types
- npm run cover
- npm test -- --coverage

after_success:
- npx codecov -f coverage/lcov.info
Expand Down
1 change: 1 addition & 0 deletions generators/app/templates/.vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"kumar-harsh.graphql-for-vscode",
"mikestead.dotenv",
"msjsdiag.debugger-for-chrome",
"orta.vscode-jest",
Expand Down
Empty file.
2 changes: 1 addition & 1 deletion generators/app/templates/_gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
node_modules
/.pnp
.pnp.js

Expand Down
14 changes: 14 additions & 0 deletions generators/app/templates/codegen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
overwrite: true
schema: 'server/src/schemas/**/*.gql'
documents: 'src/**/*.{ts,tsx}'
generates:
src/models/generated.tsx:
- typescript
- typescript-operations
server/models.ts:
- typescript
- typescript-resolvers
pluckConfig:
modules:
- name: 'babel-plugin-relay/macro'
identifier: 'graphql'
24 changes: 19 additions & 5 deletions generators/app/templates/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@
},
"homepage": "https://github.com/<%= githubUsername %>/<%= appname %>#readme",
"scripts": {
"clean": "rimraf coverage build *.log*",
"start": "craco start",
"prebuild": "rimraf build",
"postinstall": "cd server && npm install",
"clean": "rimraf coverage build *.log* src/__generated__ src/models/generated.ts server/models.ts",
"prestart": "npm run clean && concurrently \"npm:codegen\" \"npm:relay\"",
"start": "concurrently \"npm:graphql\" \"npm:craco\" \"npm:w:codegen\" \"npm:w:relay\"",
"craco": "craco start",
"prebuild": "npm run clean",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject",
"check-types": "tsc --noEmit",
"precover": "rimraf coverage",
"cover": "npm test -- --coverage"
"relay": "relay-compiler --src src --schema server/src/schemas/Root.gql --language typescript --artifactDirectory src/__generated__",
"w:relay": "npm run relay -- --watch",
"codegen": "graphql-codegen -c codegen.yml",
"w:codegen": "npm run codegen -- -w",
"graphql": "cd server && npm start"
},
"husky": {
"hooks": {
Expand All @@ -39,6 +46,7 @@
},
"prettier": {
"arrowParens": "avoid",
"ignorePath": "src/__generated__",
"proseWrap": "always",
"semi": false,
"singleQuote": true,
Expand Down Expand Up @@ -85,5 +93,11 @@
"not dead",
"not ie <= 11",
"not op_mini all"
]
],
"babelMacros": {
"relay": {
"artifactDirectory": "src/__generated__"
}
},
"proxy": "http://localhost:4000"
}
23 changes: 23 additions & 0 deletions generators/app/templates/server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "server",
"private": true,
"version": "0.1.0",
"description": "Express GraphQL Server",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "ts-node src/server.ts"
},
"dependencies": {
"express": "^4",
"express-graphql": "^0.8",
"graphql": "^14",
"graphql-relay": "^0.6",
"ts-node": "^8",
"typescript": "^3"
},
"devDependencies": {
"@types/express": "^4",
"@types/express-graphql": "^0.8",
"@types/graphql": "^14"
}
}
11 changes: 11 additions & 0 deletions generators/app/templates/server/src/loadSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { buildSchema } from 'graphql'
import { readFileSync } from 'fs'
import { join } from 'path'

export default function loadSchema(name: string) {
return buildSchema(
readFileSync(join(__dirname, 'schemas', `${name}.gql`), {
encoding: 'utf-8',
}),
)
}
7 changes: 7 additions & 0 deletions generators/app/templates/server/src/models/Root.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Query } from '../../models'

export default class Root implements Query {
public get hello() {
return 'Hello world!'
}
}
4 changes: 4 additions & 0 deletions generators/app/templates/server/src/schemas/Root.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type Query {
id: ID
hello: String
}
18 changes: 18 additions & 0 deletions generators/app/templates/server/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import express = require('express')
import graphqlHTTP = require('express-graphql')

import Root from './models/Root'

import loadSchema from './loadSchema'

var app = express()
app.use(
'/graphql',
graphqlHTTP({
schema: loadSchema('Root'),
rootValue: new Root(),
graphiql: true,
}),
)
app.listen(4000)
console.log('Running a GraphQL API server at localhost:4000/graphql')
11 changes: 11 additions & 0 deletions generators/app/templates/server/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "@jedmao/tsconfig",
"compilerOptions": {
"esModuleInterop": true,
"module": "commonjs",
"noEmit": true,
"skipLibCheck": true,
"target": "es2015"
},
"include": ["src/**/*.ts"]
}
68 changes: 59 additions & 9 deletions generators/app/templates/src/components/Home/Home.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import { DeepPartial } from 'redux'
import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils'

import RootState from 'store/RootState'
import darkTheme from 'themes/dark'
Expand All @@ -9,21 +10,59 @@ import { renderWithRedux } from 'utils/test'
import Home, { HomeProps } from '.'

describe('Home', () => {
it('renders translated text', () => {
const learnText = render({
const defaultState = {
global: {
theme: darkTheme,
},
}
let environment: ReturnType<typeof createMockEnvironment>

beforeEach(() => {
environment = createMockEnvironment()
})

it('renders loading state', () => {
const rendered = render()

expect(rendered.getByTestId('spinner')).toBeDefined()
})

it('renders mock value for field "hello"', () => {
const rendered = render()

environment.mock.resolveMostRecentOperation(MockPayloadGenerator.generate)

expect(rendered.getByText('<mock-value-for-field-"hello">')).toBeDefined()
})

it('renders error state', () => {
const rendered = render()
const err = new Error('ERROR_MESSAGE')

environment.mock.rejectMostRecentOperation(err)

expect(rendered.getByText(`Error! ${err.message}`)).toBeDefined()
})

it('renders a cart subtotal from state', () => {
const amount = 42
const rendered = render({
state: {
cart: {
subtotal: {
amount: 0,
amount,
},
},
global: {
theme: darkTheme,
},
},
}).getByText(translations['home.learn'])
})

expect(learnText).not.toBeNull()
expect(rendered.getByText(amount.toString())).toBeDefined()
})

it('renders translated text', () => {
const learnText = render().getByText(translations['home.learn'])

expect(learnText).toBeDefined()
})

const defaultProps: HomeProps = {}
Expand All @@ -35,6 +74,17 @@ describe('Home', () => {
props?: Partial<HomeProps>
state?: DeepPartial<RootState>
} = {}) {
return renderWithRedux(<Home {...defaultProps} {...props} />, { state })
return renderWithRedux(
<Home
{...{
...defaultProps,
environment,
...props,
}}
/>,
{
state: { ...defaultState, ...state },
},
)
}
})
Loading