Merge branch 'dev' into feature/fmp_to_postgresql_t17

This commit is contained in:
abpwrs committed 2020-04-08 13:46:29 -05:00
commit f1fc3319de
58 files changed
+11826 -305

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
---
name: Issue Template
about: A template for new ZenHub stories
title: ''
labels: ''
assignees: ''
---
As a <!-- [developer / logged-in-user] -->
I want to <!-- Ex: be able to visit the /login page -->
So that <!-- Ex: I can login to my account -->
### Acceptance Criteria:
The following needs to be completed for this story to be "done":
- [ ] ...
### Notes:
Helpful information to complete the task, suggestions on how to do so:
- ...
+13
View File
@@ -0,0 +1,13 @@
### Description:
Closes issue # ...
### Notes:
### Screenshots (Optional):
### Checklist:
- [ ] 90% Test Coverage
-25
View File
@@ -1,25 +0,0 @@
name: Build project and Run Lighthouse CI
on: [push]
jobs:
lhci:
name: Lighthouse CI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
node-version: 10.x
- name: npm install, build
run: |
pushd pwa && npm install
npm run build && popd
- name: run Lighthouse CI
run: |
npm install -g @lhci/cli@0.3.x
lhci autorun || echo "LHCI failed!"
- name: Upload lighthouse report
uses: actions/upload-artifact@v1
with:
name: lighthouse
path: ./.lighthouseci
+5 -3
View File
@@ -16,8 +16,10 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: pushd pwa && npm install && popd
- run: pushd pwa && npm run build && popd
- run: pushd pwa && npm test && popd
- run: docker-compose -f dev-docker-compose.yml up --detach
- run: docker exec haiti_pwa npm run test:cov
- run: docker exec haiti_pwa npm run cy:run
- run: docker exec haiti_pwa npm run build
- run: docker-compose -f dev-docker-compose.yml down
env:
CI: true
+3
View File
@@ -1,3 +1,6 @@
**/.lighthouseci/**
env/*
.vscode/*
# postgres persisted data
pgdata/
-11
View File
@@ -1,11 +0,0 @@
{
"ci": {
"collect": {
"staticDistDir": "./pwa/build",
"numberOfRuns": 3
},
"upload": {
"target": "temporary-public-storage"
}
}
}
+3 -3
View File
@@ -13,7 +13,7 @@
The application runs on docker, which can be installed [here](https://docs.docker.com/install/).
If you're a linux user, you'll also need to install [docker-compose](https://docs.docker.com/compose/install/) separately. Otherwise, it's included in the Mac and PC versions of "Docker Desktop"
- The PWA and accompanying database can be started by running the command `docker-compose up` from the root directory.
- `docker-compose down` will then stop the docker processes from running in the background.
- Once the images have been built and the processes started, the PWA can be seen at your localhost, at the port specified in [docker-compose.yml](https://github.com/Haiti-Distributed-Healthcare-System/hdhs/blob/dev/docker-compose.yml).
- The PWA, API, Database, and Swagger documentation can be started by running the command `docker-compose -f dev-docker-compose.yml up` from the root directory.
- `docker-compose -f dev-docker-compose.yml down` will then stop the docker processes from running in the background.
- Once the images have been built and the processes started, the PWA can be seen at your localhost, at the port specified in [dev-docker-compose.yml](https://github.com/Haiti-Distributed-Healthcare-System/hdhs/blob/dev/dev-docker-compose.yml).
- More tips to troubleshoot docker are available [here](https://github.com/Haiti-Distributed-Healthcare-System/hdhs/wiki/Using-Docker).
View File
File renamed without changes.
+59
View File
@@ -0,0 +1,59 @@
version: '3'
services:
pwa:
build:
context: ./pwa
volumes:
# These dirs can be changed outside of the conatiner, and reflected within.
# All other files require a rebuild:
# docker-compose -f dev-docker-compose.yml up --build
- ./pwa/src:/data/src
- ./pwa/cypress:/data/cypress
- ./pwa/public:/data/public
- ./pwa/coverage:/data/coverage
ports:
- "3000:3000"
container_name: haiti_pwa
environment:
environment: "docker"
database:
image: "postgres" # use latest official postgres version
volumes:
- "./pgdata:/var/lib/postgresql/data" # persist data even if container shuts down
ports:
- "5432:5432"
expose:
- "5422"
environment:
POSTGRES_DB: app_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: HaitiCHI
environment: "docker"
api:
image: postgrest/postgrest
ports:
- "4000:4000"
expose:
- "4000"
links:
- database:database
environment:
PGRST_DB_URI: postgres://postgres:HaitiCHI@database:5432/app_db
PGRST_DB_SCHEMA: public
PGRST_DB_ANON_ROLE: postgres
PGRST_SERVER_PORT: 4000
environment: "docker"
depends_on:
- database
swagger:
image: swaggerapi/swagger-ui
ports:
- "4001:8080"
expose:
- "8080"
environment:
API_URL: http://localhost:4000
environment: "docker"
+10 -6
View File
@@ -1,11 +1,14 @@
version: '3'
version: '3.7'
services:
pwa:
build:
context: ./pwa
dockerfile: Dockerfile-pi
volumes:
- ./pwa/:/app
- type: bind
source: ./pwa/build
target: /data/app/build
ports:
- "3000:3000"
container_name: haiti_pwa
@@ -13,10 +16,11 @@ services:
database:
image: "postgres" # use latest official postgres version
env_file:
- ./db/database.env # configure postgres
- ./database.env # configure postgres
volumes:
- database-data:/var/lib/postgresql/data/ # persist data even if container shuts down
- "database-data:/var/lib/postgresql/data/" # persist data even if container shuts down
ports:
- "5432:5432"
- "5432:5432"
volumes:
database-data: # named volumes can be managed easier using docker-compose
database-data: # named volumes can be managed easier using docker-compose
+1
View File
@@ -0,0 +1 @@
SKIP_PREFLIGHT_CHECK=true
-3
View File
@@ -8,9 +8,6 @@
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
+4 -5
View File
@@ -1,15 +1,14 @@
FROM node:12.10.0
COPY package*.json ./data/
FROM cypress/base:12.16.0
WORKDIR /data
COPY package*.json ./
RUN npm install
ENV PATH /data/node_modules/.bin:$PATH
COPY . /data/app
WORKDIR /data/app
COPY . ./
EXPOSE 3000
+16
View File
@@ -0,0 +1,16 @@
FROM node:12.10.0
COPY package*.json ./data/
WORKDIR /data
RUN npm install
ENV PATH /data/node_modules/.bin:$PATH
COPY . /data/app
WORKDIR /data/app
EXPOSE 3000
CMD ["npm", "run", "build"]
+1
View File
@@ -0,0 +1 @@
{"video": false}
+22
View File
@@ -0,0 +1,22 @@
# Cypress.io end-to-end tests
[Cypress.io](https://www.cypress.io) is an open source, MIT licensed end-to-end test runner
## Folder structure
These folders hold end-to-end tests and supporting files for the Cypress Test Runner.
- [fixtures](fixtures) holds optional JSON data for mocking, [read more](https://on.cypress.io/fixture)
- [integration](integration) holds the actual test files, [read more](https://on.cypress.io/writing-and-organizing-tests)
- [plugins](plugins) allow you to customize how tests are loaded, [read more](https://on.cypress.io/plugins)
- [support](support) file runs before all tests and is a great place to write or load additional custom commands, [read more](https://on.cypress.io/writing-and-organizing-tests#Support-file)
## `cypress.json` file
You can configure project options in the [../cypress.json](../cypress.json) file, see [Cypress configuration doc](https://on.cypress.io/configuration).
## More information
- [https://github.com/cypress.io/cypress](https://github.com/cypress.io/cypress)
- [https://docs.cypress.io/](https://docs.cypress.io/)
- [Writing your first Cypress test](http://on.cypress.io/intro)
+5
View File
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
+12
View File
@@ -0,0 +1,12 @@
// enables intelligent code completion for Cypress commands
// https://on.cypress.io/intelligent-code-completion
/// <reference types="cypress" />
describe('Homepage', () => {
it('shows login button', function () {
cy.visit('http://localhost:3000')
cy.get('.am-button').contains('Login')
})
})
export {};
+50
View File
@@ -0,0 +1,50 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/no-var-requires */
const webpack = require('@cypress/webpack-preprocessor');
const webpackOptions = {
mode: 'development',
// make sure the source maps work
devtool: 'eval-source-map',
// webpack will transpile TS and JS files
resolve: {
extensions: ['.ts', '.js', '.tsx', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx|mjs)$/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: ['@babel/plugin-proposal-class-properties'],
},
},
{
test: /\.(ts|tsx)$/,
loader: ['babel-loader', 'awesome-typescript-loader'],
},
{
test: /\.css$/,
exclude: [/node_modules/],
use: ['style-loader', 'css-loader'],
},
{
test: /\.svg$/,
exclude: [/node_modules/],
use: ['svg-url-loader'],
},
],
},
};
const options = {
webpackOptions,
watchOptions: {},
};
module.exports = (on, config) => {
on('file:preprocessor', webpack(options));
return config;
};
+14
View File
@@ -0,0 +1,14 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "../tsconfig.json",
"include": [
"../node_modules/cypress",
"*/*.ts"
],
"compilerOptions": {
"strict": false,
"baseUrl": "../node_modules",
"target": "es5",
"lib": ["es7", "dom"],
"esModuleInterop": true,
"jsx": "react",
"inlineSourceMap": true,
"skipLibCheck": true
}
}
+3012 -143
View File
File diff suppressed because it is too large. Load diff
+28
View File
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^4.0.5",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
@@ -10,6 +11,8 @@
"@types/node": "^12.12.26",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"antd-mobile": "^2.3.1",
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "3.3.1",
@@ -19,11 +22,24 @@
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:cov": "react-scripts test --coverage --watchAll=false",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"jest": {
"coverageThreshold": {
"global": {
"statements": 90
}
},
"coverageReporters": [
"lcov"
]
},
"browserslist": {
"production": [
">0.2%",
@@ -35,5 +51,17 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@cypress/webpack-preprocessor": "^4.1.3",
"@testing-library/react-hooks": "^3.2.1",
"awesome-typescript-loader": "^5.2.1",
"babel-loader": "^8.1.0",
"babel-plugin-import": "^1.13.0",
"cypress": "^4.2.0",
"react-test-renderer": "^16.13.1",
"svg-url-loader": "^5.0.0",
"ts-loader": "^6.2.2",
"webpack": "^4.42.1"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

+2 -2
View File
@@ -9,7 +9,7 @@
name="description"
content="Healthcare Application for the Haiti Community Healthcare Initiative"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/appIcon512.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
@@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>HDHS Patient Tool</title>
</head>
<body>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

+1 -36
View File
@@ -7,42 +7,7 @@
"start_url": ".",
"icons": [
{
"src": "images/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "images/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "images/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "images/icons/icon-512x512.png",
"src": "appIcon512.png",
"sizes": "512x512",
"type": "image/png"
}
-38
View File
@@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
+1 -1
View File
@@ -4,6 +4,6 @@ import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
const linkElement = getByText(/Login/i);
expect(linkElement).toBeInTheDocument();
});
+9 -20
View File
@@ -1,26 +1,15 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';
import React from "react";
import "./scss/App.scss";
import "./antd-mobile.css";
import Login from "./components/Login";
const App = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
<>
<Login />
</>
);
}
};
export default App;
File diff suppressed because it is too large. Load diff
+10
View File
@@ -0,0 +1,10 @@
/*!
*
* antd-mobile v2.3.1
*
* Copyright 2015-present, Alipay, Inc.
* All rights reserved.
*
*/
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}.am-fade-appear,.am-fade-enter{opacity:0}.am-fade-appear,.am-fade-enter,.am-fade-leave{-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:cubic-bezier(.55,0,.55,.2);animation-timing-function:cubic-bezier(.55,0,.55,.2);-webkit-animation-play-state:paused;animation-play-state:paused}.am-fade-appear.am-fade-appear-active,.am-fade-enter.am-fade-enter-active{-webkit-animation-name:amFadeIn;animation-name:amFadeIn;-webkit-animation-play-state:running;animation-play-state:running}.am-fade-leave.am-fade-leave-active{-webkit-animation-name:amFadeOut;animation-name:amFadeOut;-webkit-animation-play-state:running;animation-play-state:running}@-webkit-keyframes amFadeIn{0%{opacity:0}to{opacity:1}}@keyframes amFadeIn{0%{opacity:0}to{opacity:1}}@-webkit-keyframes amFadeOut{0%{opacity:1}to{opacity:0}}@keyframes amFadeOut{0%{opacity:1}to{opacity:0}}.am-slide-up-appear,.am-slide-up-enter{-webkit-transform:translateY(100%);-ms-transform:translateY(100%);transform:translateY(100%)}.am-slide-up-appear,.am-slide-up-enter,.am-slide-up-leave{-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:cubic-bezier(.55,0,.55,.2);animation-timing-function:cubic-bezier(.55,0,.55,.2);-webkit-animation-play-state:paused;animation-play-state:paused}.am-slide-up-appear.am-slide-up-appear-active,.am-slide-up-enter.am-slide-up-enter-active{-webkit-animation-name:amSlideUpIn;animation-name:amSlideUpIn;-webkit-animation-play-state:running;animation-play-state:running}.am-slide-up-leave.am-slide-up-leave-active{-webkit-animation-name:amSlideUpOut;animation-name:amSlideUpOut;-webkit-animation-play-state:running;animation-play-state:running}@-webkit-keyframes amSlideUpIn{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes amSlideUpIn{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes amSlideUpOut{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}@keyframes amSlideUpOut{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}.am.am-zoom-enter,.am.am-zoom-leave{display:block}.am-zoom-appear,.am-zoom-enter{opacity:0;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:both;animation-fill-mode:boLine truncated
/*# sourceMappingURL=antd-mobile.min.css.map*/
+11
View File
@@ -0,0 +1,11 @@
import React from "react";
import { render } from "@testing-library/react";
import LandingPage from "./LandingPage";
test("renders learn react link", () => {
const { getByText } = render(<LandingPage />);
const lookupButtonElement = getByText(/Patient Lookup/i);
const newPatientButtonElement = getByText(/New Patient/i);
expect(lookupButtonElement).toBeInTheDocument();
expect(newPatientButtonElement).toBeInTheDocument();
});
+34
View File
@@ -0,0 +1,34 @@
import React, { ReactElement } from "react";
import { WhiteSpace, Button } from "antd-mobile";
import Icon, { UserAddOutlined } from "@ant-design/icons";
import "../scss/LandingPage.scss";
export default function LandingPage(): ReactElement {
/* istanbul ignore next */
const patientLookup = () => {
console.log("Clicked Patient Lookup");
};
/* istanbul ignore next */
const newPatient = () => {
console.log("Clicked New Patient");
};
return (
<div id="landing-wrapper">
<div className="button-flex">
<Button onClick={patientLookup} icon="search">
Patient Lookup
</Button>
<WhiteSpace />
</div>
<br />
<div className="button-flex">
<Button icon={<UserAddOutlined />} onClick={newPatient}>
New Patient
</Button>
<WhiteSpace />
</div>
</div>
);
}
+31
View File
@@ -0,0 +1,31 @@
import React, { ReactElement } from 'react'
import useApi from '../hooks/useApi'
import {InputItem, Button} from 'antd-mobile'
import '../scss/Login.scss'
export default function Login(): ReactElement {
const {response, error, isLoading} = useApi("")
return (
<>
<div id="loginWrapper">
<div id="loginGrid">
<div id="iconCard">
<div id="imgWrap">
<img src="/logoIcon512.jpg" alt="CHI Logo"></img>
</div>
</div>
<InputItem placeholder="username"/>
<InputItem placeholder="password" type="password"/>
<div id="buttonFlex">
<Button>Login</Button>
</div>
</div>
</div>
<p>Loading API Fetch: {isLoading.toString()}</p>
{response &&
<p>API Fetch Result: {JSON.stringify(response)}</p>
}
</>
)
}
+16
View File
@@ -0,0 +1,16 @@
import { renderHook } from '@testing-library/react-hooks'
import useApi from './useApi'
test('should return loading and then a response', async () => {
const { result, waitForNextUpdate } = renderHook(() => useApi(""))
expect(result.current.response).toBeNull()
expect(result.current.error).toBeNull()
expect(result.current.isLoading).toBeTruthy()
await waitForNextUpdate()
expect(result.current.response).toBeTruthy()
expect(result.current.error).toBeNull()
expect(result.current.isLoading).toBeFalsy()
})
+35
View File
@@ -0,0 +1,35 @@
import React from 'react'
const useApi = (route: string) => {
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
/* istanbul ignore next */
const getUrl = () => {
if (process.env.environment === "docker"){
return "http://api:4000/"
}
else {
return "http://localhost:4000/"
}
}
React.useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
const res = await fetch(getUrl() + route);
const json = await res.json();
setResponse(json);
setIsLoading(false)
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
return { response, error, isLoading };
};
export default useApi
+2 -1
View File
@@ -1,6 +1,7 @@
/* istanbul ignore file */
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './scss/index.scss';
import App from './App';
import * as serviceWorker from './serviceWorker';
-7
View File
@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

+7
View File
@@ -0,0 +1,7 @@
.am-button:hover {
cursor: pointer;
}
#root {
height: 100vh;
}
+21
View File
@@ -0,0 +1,21 @@
.button-flex {
animation-name: fade-in;
animation-duration: 2s;
display: flex;
justify-content: center;
margin: 2em 0;
.am-button {
background-color: #9bd7ff;
width: 40%;
&.am-button-active {
background-color: #ccebff;
}
}
}
#landing-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
}
+71
View File
@@ -0,0 +1,71 @@
// @import '_include-media.scss';
// @include media("<desktop") {
#loginWrapper {
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
padding: 0 2rem;
#loginGrid {
display: grid;
align-items: center;
justify-content: center;
height: 45%;
#iconCard {
animation-name: fade-in-from-up;
animation-duration: 1.5s;
justify-content: center;
align-items: center;
flex-direction: row;
#imgWrap {
width: 28%;
margin: auto;
padding: .2rem;
background-color: #eee;
border-radius: 5px;
img {
display: block;
width: 100%;
max-width: 512px;
height: auto;
margin: auto;
}
}
}
input {
animation-name: fade-in;
animation-duration: 2s;
}
#buttonFlex{
animation-name: fade-in;
animation-duration: 2s;
display: flex;
justify-content: center;
.am-button {
background-color: #9BD7FF;
width: 40%;
&.am-button-active {
background-color: #ccebff;
}
}
}
}
}
$animationDelay: 1;
@for $i from 1 through 15 {
.loginGrid :nth-child(#{$i}) {
animation-delay: #{0.3+($i)/30}s;
}
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes fade-in-from-up {
0% { opacity: 0; transform: translateY(-100px)}
100% { opacity: 1; }
}
+565
View File
@@ -0,0 +1,565 @@
@charset "UTF-8";
// _ _ _ _ _
// (_) | | | | | (_)
// _ _ __ ___| |_ _ __| | ___ _ __ ___ ___ __| |_ __ _
// | | '_ \ / __| | | | |/ _` |/ _ \ | '_ ` _ \ / _ \/ _` | |/ _` |
// | | | | | (__| | |_| | (_| | __/ | | | | | | __/ (_| | | (_| |
// |_|_| |_|\___|_|\__,_|\__,_|\___| |_| |_| |_|\___|\__,_|_|\__,_|
//
// Simple, elegant and maintainable media queries in Sass
// v1.4.9
//
// https://eduardoboucas.github.io/include-media
//
// Authors: Eduardo Boucas (@eduardoboucas)
// Hugo Giraudel (@hugogiraudel)
//
// This project is licensed under the terms of the MIT license
////
/// include-media library public configuration
/// @author Eduardo Boucas
/// @access public
////
///
/// Creates a list of global breakpoints
///
/// @example scss - Creates a single breakpoint with the label `phone`
/// $breakpoints: ('phone': 320px);
///
$breakpoints: (
'phone': 320px,
'tablet': 768px,
'desktop': 1024px
) !default;
///
/// Creates a list of static expressions or media types
///
/// @example scss - Creates a single media type (screen)
/// $media-expressions: ('screen': 'screen');
///
/// @example scss - Creates a static expression with logical disjunction (OR operator)
/// $media-expressions: (
/// 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)'
/// );
///
$media-expressions: (
'screen': 'screen',
'print': 'print',
'handheld': 'handheld',
'landscape': '(orientation: landscape)',
'portrait': '(orientation: portrait)',
'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)',
'retina3x': '(-webkit-min-device-pixel-ratio: 3), (min-resolution: 350dpi), (min-resolution: 3dppx)'
) !default;
///
/// Defines a number to be added or subtracted from each unit when declaring breakpoints with exclusive intervals
///
/// @example scss - Interval for pixels is defined as `1` by default
/// @include media('>128px') {}
///
/// /* Generates: */
/// @media (min-width: 129px) {}
///
/// @example scss - Interval for ems is defined as `0.01` by default
/// @include media('>20em') {}
///
/// /* Generates: */
/// @media (min-width: 20.01em) {}
///
/// @example scss - Interval for rems is defined as `0.1` by default, to be used with `font-size: 62.5%;`
/// @include media('>2.0rem') {}
///
/// /* Generates: */
/// @media (min-width: 2.1rem) {}
///
$unit-intervals: (
'px': 1,
'em': 0.01,
'rem': 0.1,
'': 0
) !default;
///
/// Defines whether support for media queries is available, useful for creating separate stylesheets
/// for browsers that don't support media queries.
///
/// @example scss - Disables support for media queries
/// $im-media-support: false;
/// @include media('>=tablet') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
$im-media-support: true !default;
///
/// Selects which breakpoint to emulate when support for media queries is disabled. Media queries that start at or
/// intercept the breakpoint will be displayed, any others will be ignored.
///
/// @example scss - This media query will show because it intercepts the static breakpoint
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// @include media('>=tablet') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
/// @example scss - This media query will NOT show because it does not intercept the desktop breakpoint
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'tablet';
/// @include media('>=desktop') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* No output */
///
$im-no-media-breakpoint: 'desktop' !default;
///
/// Selects which media expressions are allowed in an expression for it to be used when media queries
/// are not supported.
///
/// @example scss - This media query will show because it intercepts the static breakpoint and contains only accepted media expressions
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// $im-no-media-expressions: ('screen');
/// @include media('>=tablet', 'screen') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* Generates: */
/// .foo {
/// color: tomato;
/// }
///
/// @example scss - This media query will NOT show because it intercepts the static breakpoint but contains a media expression that is not accepted
/// $im-media-support: false;
/// $im-no-media-breakpoint: 'desktop';
/// $im-no-media-expressions: ('screen');
/// @include media('>=tablet', 'retina2x') {
/// .foo {
/// color: tomato;
/// }
/// }
///
/// /* No output */
///
$im-no-media-expressions: ('screen', 'portrait', 'landscape') !default;
////
/// Cross-engine logging engine
/// @author Hugo Giraudel
/// @access private
////
///
/// Log a message either with `@error` if supported
/// else with `@warn`, using `feature-exists('at-error')`
/// to detect support.
///
/// @param {String} $message - Message to log
///
@function im-log($message) {
@if feature-exists('at-error') {
@error $message;
} @else {
@warn $message;
$_: noop();
}
@return $message;
}
///
/// Wrapper mixin for the log function so it can be used with a more friendly
/// API than `@if im-log('..') {}` or `$_: im-log('..')`. Basically, use the function
/// within functions because it is not possible to include a mixin in a function
/// and use the mixin everywhere else because it's much more elegant.
///
/// @param {String} $message - Message to log
///
@mixin log($message) {
@if im-log($message) {}
}
///
/// Function with no `@return` called next to `@warn` in Sass 3.3
/// to trigger a compiling error and stop the process.
///
@function noop() {}
///
/// Determines whether a list of conditions is intercepted by the static breakpoint.
///
/// @param {Arglist} $conditions - Media query conditions
///
/// @return {Boolean} - Returns true if the conditions are intercepted by the static breakpoint
///
@function im-intercepts-static-breakpoint($conditions...) {
$no-media-breakpoint-value: map-get($breakpoints, $im-no-media-breakpoint);
@if not $no-media-breakpoint-value {
@if im-log('`#{$im-no-media-breakpoint}` is not a valid breakpoint.') {}
}
@each $condition in $conditions {
@if not map-has-key($media-expressions, $condition) {
$operator: get-expression-operator($condition);
$prefix: get-expression-prefix($operator);
$value: get-expression-value($condition, $operator);
@if ($prefix == 'max' and $value <= $no-media-breakpoint-value) or
($prefix == 'min' and $value > $no-media-breakpoint-value) {
@return false;
}
} @else if not index($im-no-media-expressions, $condition) {
@return false;
}
}
@return true;
}
////
/// Parsing engine
/// @author Hugo Giraudel
/// @access private
////
///
/// Get operator of an expression
///
/// @param {String} $expression - Expression to extract operator from
///
/// @return {String} - Any of `>=`, `>`, `<=`, `<`, `≥`, `≤`
///
@function get-expression-operator($expression) {
@each $operator in ('>=', '>', '<=', '<', '≥', '≤') {
@if str-index($expression, $operator) {
@return $operator;
}
}
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('No operator found in `#{$expression}`.');
}
///
/// Get dimension of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract dimension from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {String} - `width` or `height` (or potentially anything else)
///
@function get-expression-dimension($expression, $operator) {
$operator-index: str-index($expression, $operator);
$parsed-dimension: str-slice($expression, 0, $operator-index - 1);
$dimension: 'width';
@if str-length($parsed-dimension) > 0 {
$dimension: $parsed-dimension;
}
@return $dimension;
}
///
/// Get dimension prefix based on an operator
///
/// @param {String} $operator - Operator
///
/// @return {String} - `min` or `max`
///
@function get-expression-prefix($operator) {
@return if(index(('<', '<=', '≤'), $operator), 'max', 'min');
}
///
/// Get value of an expression, based on a found operator
///
/// @param {String} $expression - Expression to extract value from
/// @param {String} $operator - Operator from `$expression`
///
/// @return {Number} - A numeric value
///
@function get-expression-value($expression, $operator) {
$operator-index: str-index($expression, $operator);
$value: str-slice($expression, $operator-index + str-length($operator));
@if map-has-key($breakpoints, $value) {
$value: map-get($breakpoints, $value);
} @else {
$value: to-number($value);
}
$interval: map-get($unit-intervals, unit($value));
@if not $interval {
// It is not possible to include a mixin inside a function, so we have to
// rely on the `im-log(..)` function rather than the `log(..)` mixin. Because
// functions cannot be called anywhere in Sass, we need to hack the call in
// a dummy variable, such as `$_`. If anybody ever raise a scoping issue with
// Sass 3.3, change this line in `@if im-log(..) {}` instead.
$_: im-log('Unknown unit `#{unit($value)}`.');
}
@if $operator == '>' {
$value: $value + $interval;
} @else if $operator == '<' {
$value: $value - $interval;
}
@return $value;
}
///
/// Parse an expression to return a valid media-query expression
///
/// @param {String} $expression - Expression to parse
///
/// @return {String} - Valid media query
///
@function parse-expression($expression) {
// If it is part of $media-expressions, it has no operator
// then there is no need to go any further, just return the value
@if map-has-key($media-expressions, $expression) {
@return map-get($media-expressions, $expression);
}
$operator: get-expression-operator($expression);
$dimension: get-expression-dimension($expression, $operator);
$prefix: get-expression-prefix($operator);
$value: get-expression-value($expression, $operator);
@return '(#{$prefix}-#{$dimension}: #{$value})';
}
///
/// Slice `$list` between `$start` and `$end` indexes
///
/// @access private
///
/// @param {List} $list - List to slice
/// @param {Number} $start [1] - Start index
/// @param {Number} $end [length($list)] - End index
///
/// @return {List} Sliced list
///
@function slice($list, $start: 1, $end: length($list)) {
@if length($list) < 1 or $start > $end {
@return ();
}
$result: ();
@for $i from $start through $end {
$result: append($result, nth($list, $i));
}
@return $result;
}
////
/// String to number converter
/// @author Hugo Giraudel
/// @access private
////
///
/// Casts a string into a number
///
/// @param {String | Number} $value - Value to be parsed
///
/// @return {Number}
///
@function to-number($value) {
@if type-of($value) == 'number' {
@return $value;
} @else if type-of($value) != 'string' {
$_: im-log('Value for `to-number` should be a number or a string.');
}
$first-character: str-slice($value, 1, 1);
$result: 0;
$digits: 0;
$minus: ($first-character == '-');
$numbers: ('0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9);
// Remove +/- sign if present at first character
@if ($first-character == '+' or $first-character == '-') {
$value: str-slice($value, 2);
}
@for $i from 1 through str-length($value) {
$character: str-slice($value, $i, $i);
@if not (index(map-keys($numbers), $character) or $character == '.') {
@return to-length(if($minus, -$result, $result), str-slice($value, $i))
}
@if $character == '.' {
$digits: 1;
} @else if $digits == 0 {
$result: $result * 10 + map-get($numbers, $character);
} @else {
$digits: $digits * 10;
$result: $result + map-get($numbers, $character) / $digits;
}
}
@return if($minus, -$result, $result);
}
///
/// Add `$unit` to `$value`
///
/// @param {Number} $value - Value to add unit to
/// @param {String} $unit - String representation of the unit
///
/// @return {Number} - `$value` expressed in `$unit`
///
@function to-length($value, $unit) {
$units: ('px': 1px, 'cm': 1cm, 'mm': 1mm, '%': 1%, 'ch': 1ch, 'pc': 1pc, 'in': 1in, 'em': 1em, 'rem': 1rem, 'pt': 1pt, 'ex': 1ex, 'vw': 1vw, 'vh': 1vh, 'vmin': 1vmin, 'vmax': 1vmax);
@if not index(map-keys($units), $unit) {
$_: im-log('Invalid unit `#{$unit}`.');
}
@return $value * map-get($units, $unit);
}
///
/// This mixin aims at redefining the configuration just for the scope of
/// the call. It is helpful when having a component needing an extended
/// configuration such as custom breakpoints (referred to as tweakpoints)
/// for instance.
///
/// @author Hugo Giraudel
///
/// @param {Map} $tweakpoints [()] - Map of tweakpoints to be merged with `$breakpoints`
/// @param {Map} $tweak-media-expressions [()] - Map of tweaked media expressions to be merged with `$media-expression`
///
/// @example scss - Extend the global breakpoints with a tweakpoint
/// @include media-context(('custom': 678px)) {
/// .foo {
/// @include media('>phone', '<=custom') {
/// // ...
/// }
/// }
/// }
///
/// @example scss - Extend the global media expressions with a custom one
/// @include media-context($tweak-media-expressions: ('all': 'all')) {
/// .foo {
/// @include media('all', '>phone') {
/// // ...
/// }
/// }
/// }
///
/// @example scss - Extend both configuration maps
/// @include media-context(('custom': 678px), ('all': 'all')) {
/// .foo {
/// @include media('all', '>phone', '<=custom') {
/// // ...
/// }
/// }
/// }
///
@mixin media-context($tweakpoints: (), $tweak-media-expressions: ()) {
// Save global configuration
$global-breakpoints: $breakpoints;
$global-media-expressions: $media-expressions;
// Update global configuration
$breakpoints: map-merge($breakpoints, $tweakpoints) !global;
$media-expressions: map-merge($media-expressions, $tweak-media-expressions) !global;
@content;
// Restore global configuration
$breakpoints: $global-breakpoints !global;
$media-expressions: $global-media-expressions !global;
}
////
/// include-media public exposed API
/// @author Eduardo Boucas
/// @access public
////
///
/// Generates a media query based on a list of conditions
///
/// @param {Arglist} $conditions - Media query conditions
///
/// @example scss - With a single set breakpoint
/// @include media('>phone') { }
///
/// @example scss - With two set breakpoints
/// @include media('>phone', '<=tablet') { }
///
/// @example scss - With custom values
/// @include media('>=358px', '<850px') { }
///
/// @example scss - With set breakpoints with custom values
/// @include media('>desktop', '<=1350px') { }
///
/// @example scss - With a static expression
/// @include media('retina2x') { }
///
/// @example scss - Mixing everything
/// @include media('>=350px', '<tablet', 'retina3x') { }
///
@mixin media($conditions...) {
@if ($im-media-support and length($conditions) == 0) or
(not $im-media-support and im-intercepts-static-breakpoint($conditions...)) {
@content;
} @else if ($im-media-support and length($conditions) > 0) {
@media #{unquote(parse-expression(nth($conditions, 1)))} {
// Recursive call
@include media(slice($conditions, 2)...) {
@content;
}
}
}
}
View File
Whitespace-only changes.
@@ -5,9 +5,12 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #e8ffd9;
height: 100%;
width: 100%;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
}
+1
View File
@@ -1,3 +1,4 @@
/* istanbul ignore file */
// This optional code is used to register a service worker.
// register() is not called by default.
+1
View File
@@ -6,6 +6,7 @@
"dom.iterable",
"esnext"
],
"strictNullChecks": false,
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,