├── .editorconfig ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── backend ├── 00 Boilerplate │ ├── .env │ ├── README.md │ ├── config │ │ ├── constants.js │ │ ├── gulp │ │ │ ├── base.js │ │ │ └── dev.js │ │ └── helpers.js │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── env.ts │ │ └── routers │ │ │ ├── index.ts │ │ │ └── login │ │ │ ├── controller.ts │ │ │ ├── index.ts │ │ │ └── router.ts │ ├── tsconfig.json │ └── tslint.json ├── 01 Add config │ ├── .env │ ├── README.md │ ├── config │ │ ├── constants.js │ │ ├── gulp │ │ │ ├── base.js │ │ │ └── dev.js │ │ ├── helpers.js │ │ └── test │ │ │ └── jest.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── env.ts │ │ ├── routers │ │ │ ├── index.ts │ │ │ └── login │ │ │ │ ├── controller.ts │ │ │ │ ├── index.ts │ │ │ │ └── router.ts │ │ └── sample.spec.ts │ ├── tsconfig.json │ └── tslint.json ├── 02 Controllers │ ├── .env │ ├── README.md │ ├── config │ │ ├── constants.js │ │ ├── gulp │ │ │ ├── base.js │ │ │ └── dev.js │ │ ├── helpers.js │ │ └── test │ │ │ └── jest.json │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── env.ts │ │ └── routers │ │ │ ├── index.ts │ │ │ └── login │ │ │ ├── controller.spec.ts │ │ │ ├── controller.ts │ │ │ ├── index.ts │ │ │ └── router.ts │ ├── tsconfig.json │ └── tslint.json └── 03 Debugging Jest │ ├── .env │ ├── .vscode │ └── launch.json │ ├── README.md │ ├── config │ ├── constants.js │ ├── gulp │ │ ├── base.js │ │ └── dev.js │ ├── helpers.js │ └── test │ │ └── jest.json │ ├── package.json │ ├── src │ ├── app.ts │ ├── env.ts │ └── routers │ │ ├── index.ts │ │ └── login │ │ ├── controller.spec.ts │ │ ├── controller.ts │ │ ├── index.ts │ │ └── router.ts │ ├── tsconfig.json │ └── tslint.json └── frontend ├── 00 Boilerplate ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── README.md ├── config │ ├── helpers.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 01 Add config ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ └── polyfills.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ ├── sample.spec.ts │ └── store.ts ├── tsconfig.json └── tslint.json ├── 02 Plain Vanilla ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ └── polyfills.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 03 Debugging Jest ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ └── polyfills.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 04 Redux Actions ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ └── polyfills.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 05 Redux Reducers ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ └── polyfills.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 06 Components ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.json │ │ ├── polyfills.js │ │ └── setupTest.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ ├── button.spec.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.spec.tsx │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── __snapshots__ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ ├── body.spec.tsx │ │ │ │ ├── body.tsx │ │ │ │ ├── header.scss │ │ │ │ ├── header.spec.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── panel.spec.tsx │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.spec.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 07 Code Coverage ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.coverage.json │ │ ├── jest.json │ │ ├── polyfills.js │ │ └── setupTest.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ ├── button.spec.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.spec.tsx │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── __snapshots__ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ ├── body.spec.tsx │ │ │ │ ├── body.tsx │ │ │ │ ├── header.scss │ │ │ │ ├── header.spec.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── panel.spec.tsx │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.spec.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 08 Reselect ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.coverage.json │ │ ├── jest.json │ │ ├── polyfills.js │ │ └── setupTest.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ ├── button.spec.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.spec.tsx │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── __snapshots__ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ ├── body.spec.tsx │ │ │ │ ├── body.tsx │ │ │ │ ├── header.scss │ │ │ │ ├── header.spec.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── panel.spec.tsx │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.spec.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ ├── selectors.spec.ts │ │ │ │ ├── selectors.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 09 Travis CI ├── .babelrc ├── .editorconfig ├── .env ├── .prettierrc ├── .travis.yml ├── .vscode │ └── launch.json ├── README.md ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.coverage.json │ │ ├── jest.json │ │ ├── polyfills.js │ │ └── setupTest.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js ├── package.json ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ ├── button.spec.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.spec.tsx │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── __snapshots__ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ ├── body.spec.tsx │ │ │ │ ├── body.tsx │ │ │ │ ├── header.scss │ │ │ │ ├── header.spec.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── panel.spec.tsx │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.spec.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ ├── selectors.spec.ts │ │ │ │ ├── selectors.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts ├── tsconfig.json └── tslint.json ├── 10 Hooks ├── 00 Boilerplate │ ├── .babelrc │ ├── .editorconfig │ ├── .env │ ├── .prettierrc │ ├── .travis.yml │ ├── .vscode │ │ └── launch.json │ ├── README.md │ ├── config │ │ ├── helpers.js │ │ ├── test │ │ │ ├── jest.coverage.json │ │ │ ├── jest.json │ │ │ ├── polyfills.js │ │ │ └── setupTest.js │ │ └── webpack │ │ │ ├── app │ │ │ ├── base.js │ │ │ ├── dev.js │ │ │ └── prod.js │ │ │ └── common.js │ ├── package.json │ ├── src │ │ ├── app.tsx │ │ ├── appProvider.tsx │ │ ├── appRouter.tsx │ │ ├── common │ │ │ ├── components │ │ │ │ ├── form │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ │ ├── button.spec.tsx │ │ │ │ │ ├── button.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── input.spec.tsx │ │ │ │ │ └── input.tsx │ │ │ │ └── panel │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ │ ├── components │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ │ ├── body.spec.tsx │ │ │ │ │ ├── body.tsx │ │ │ │ │ ├── header.scss │ │ │ │ │ ├── header.spec.tsx │ │ │ │ │ ├── header.tsx │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── panel.spec.tsx │ │ │ │ │ └── panel.tsx │ │ │ └── constants │ │ │ │ └── routes │ │ │ │ └── index.ts │ │ ├── history.ts │ │ ├── index.html │ │ ├── index.tsx │ │ ├── pages │ │ │ ├── index.ts │ │ │ ├── login │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ │ ├── actionIds.ts │ │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ │ ├── loginRequest.ts │ │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ │ └── updateLoginEntityField.ts │ │ │ │ ├── components │ │ │ │ │ ├── form.tsx │ │ │ │ │ ├── formProps.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── login.spec.ts │ │ │ │ │ └── login.ts │ │ │ │ ├── validations.spec.ts │ │ │ │ ├── validations.ts │ │ │ │ └── viewModel.ts │ │ │ ├── members │ │ │ │ ├── edit │ │ │ │ │ ├── components │ │ │ │ │ │ ├── form.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mappers.ts │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── pageContainer.tsx │ │ │ │ │ └── viewModel.ts │ │ │ │ ├── index.ts │ │ │ │ └── list │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ │ ├── actions │ │ │ │ │ ├── actionIds.ts │ │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ │ └── fetchMembers.ts │ │ │ │ │ ├── components │ │ │ │ │ ├── body.tsx │ │ │ │ │ ├── header.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── row.scss │ │ │ │ │ ├── row.tsx │ │ │ │ │ └── table.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mappers.spec.ts │ │ │ │ │ ├── mappers.ts │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ │ ├── pageContainer.tsx │ │ │ │ │ ├── reducers │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── members.spec.ts │ │ │ │ │ └── members.ts │ │ │ │ │ ├── selectors.spec.ts │ │ │ │ │ ├── selectors.ts │ │ │ │ │ └── viewModel.ts │ │ │ └── reducers.ts │ │ ├── rest-api │ │ │ ├── api │ │ │ │ ├── login.ts │ │ │ │ └── member.ts │ │ │ └── model │ │ │ │ ├── index.ts │ │ │ │ ├── loginEntity.ts │ │ │ │ └── member.ts │ │ ├── routes.tsx │ │ └── store.ts │ ├── tsconfig.json │ └── tslint.json └── 01 Testing hooks │ ├── .babelrc │ ├── .editorconfig │ ├── .env │ ├── .prettierrc │ ├── .travis.yml │ ├── .vscode │ └── launch.json │ ├── README.md │ ├── config │ ├── helpers.js │ ├── test │ │ ├── jest.coverage.json │ │ ├── jest.json │ │ ├── polyfills.js │ │ └── setupTest.js │ └── webpack │ │ ├── app │ │ ├── base.js │ │ ├── dev.js │ │ └── prod.js │ │ └── common.js │ ├── package.json │ ├── src │ ├── app.tsx │ ├── appProvider.tsx │ ├── appRouter.tsx │ ├── common │ │ ├── components │ │ │ ├── form │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── button.spec.tsx.snap │ │ │ │ │ └── input.spec.tsx.snap │ │ │ │ ├── button.spec.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── input.spec.tsx │ │ │ │ └── input.tsx │ │ │ └── panel │ │ │ │ ├── __snapshots__ │ │ │ │ └── panel.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── body.spec.tsx.snap │ │ │ │ │ └── header.spec.tsx.snap │ │ │ │ ├── body.spec.tsx │ │ │ │ ├── body.tsx │ │ │ │ ├── header.scss │ │ │ │ ├── header.spec.tsx │ │ │ │ ├── header.tsx │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── panel.spec.tsx │ │ │ │ └── panel.tsx │ │ └── constants │ │ │ └── routes │ │ │ └── index.ts │ ├── history.ts │ ├── index.html │ ├── index.tsx │ ├── pages │ │ ├── index.ts │ │ ├── login │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── loginRequest.spec.ts │ │ │ │ ├── loginRequest.ts │ │ │ │ ├── updateLoginEntityField.spec.ts │ │ │ │ └── updateLoginEntityField.ts │ │ │ ├── components │ │ │ │ ├── form.tsx │ │ │ │ ├── formProps.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── mappers.spec.ts │ │ │ ├── mappers.ts │ │ │ ├── page.tsx │ │ │ ├── pageContainer.spec.tsx │ │ │ ├── pageContainer.tsx │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── login.spec.ts │ │ │ │ └── login.ts │ │ │ ├── validations.spec.ts │ │ │ ├── validations.ts │ │ │ └── viewModel.ts │ │ ├── members │ │ │ ├── edit │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── components │ │ │ │ │ ├── form.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ └── viewModel.ts │ │ │ ├── index.ts │ │ │ └── list │ │ │ │ ├── __snapshots__ │ │ │ │ └── pageContainer.spec.tsx.snap │ │ │ │ ├── actions │ │ │ │ ├── actionIds.ts │ │ │ │ ├── fetchMembers.spec.ts │ │ │ │ └── fetchMembers.ts │ │ │ │ ├── components │ │ │ │ ├── body.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── row.scss │ │ │ │ ├── row.tsx │ │ │ │ └── table.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mappers.spec.ts │ │ │ │ ├── mappers.ts │ │ │ │ ├── page.tsx │ │ │ │ ├── pageContainer.spec.tsx │ │ │ │ ├── pageContainer.tsx │ │ │ │ ├── reducers │ │ │ │ ├── index.ts │ │ │ │ ├── members.spec.ts │ │ │ │ └── members.ts │ │ │ │ ├── selectors.spec.ts │ │ │ │ ├── selectors.ts │ │ │ │ └── viewModel.ts │ │ └── reducers.ts │ ├── rest-api │ │ ├── api │ │ │ ├── login.ts │ │ │ └── member.ts │ │ └── model │ │ │ ├── index.ts │ │ │ ├── loginEntity.ts │ │ │ └── member.ts │ ├── routes.tsx │ └── store.ts │ ├── tsconfig.json │ └── tslint.json └── 99 Readme Resources └── 03 Debugging Jest ├── 01 Add launch.json file.png └── 02 debugging_in_vscode.gif /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist/ 4 | *.orig 5 | .idea/ 6 | bundle.js 7 | package-lock.json 8 | .awcache/ 9 | yarn.lock 10 | coverage/ 11 | public/ 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=8081 3 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/config/constants.js: -------------------------------------------------------------------------------- 1 | const { resolveFromRootPath } = require('./helpers'); 2 | 3 | exports.paths = { 4 | ts: resolveFromRootPath('./src/**/*.ts'), 5 | specs: resolveFromRootPath('./src/**/*.spec.ts'), 6 | output: resolveFromRootPath('./public'), 7 | tslintConfig: resolveFromRootPath('./tslint.json'), 8 | }; 9 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.resolve(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/app.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import * as bodyParser from 'body-parser'; 3 | import { env } from './env'; 4 | import { loginRouter } from './routers'; 5 | 6 | const app = express(); 7 | app.use(bodyParser.urlencoded({ extended: true })); 8 | app.use(bodyParser.json()); 9 | 10 | app.use('/api/login', loginRouter()); 11 | app.listen(env.PORT); 12 | 13 | // tslint:disable-next-line:no-console 14 | console.log(`Running on port ${env.PORT}`); 15 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | PORT: process.env.PORT, 3 | }; 4 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/routers/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './login'; 2 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/routers/login/controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | export const LoginController = () => ({ 4 | post: (req: Request, res: Response) => { 5 | isValidLogin(req.body.login, req.body.password) ? 6 | res.sendStatus(201) : 7 | res.sendStatus(401); 8 | }, 9 | }); 10 | 11 | const isValidLogin = (login: string, password: string) => ( 12 | login === 'admin' && 13 | password === 'test' 14 | ); 15 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/routers/login/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './router'; 2 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/src/routers/login/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { LoginController } from './controller'; 3 | 4 | export const loginRouter = () => { 5 | const router = Router(); 6 | const controller = LoginController(); 7 | router.route('/') 8 | .post(controller.post); 9 | 10 | return router; 11 | }; 12 | -------------------------------------------------------------------------------- /backend/00 Boilerplate/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "sourceMap": true, 9 | "noLib": false, 10 | "outDir": "public" 11 | }, 12 | "compileOnSave": false, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /backend/01 Add config/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=8081 3 | -------------------------------------------------------------------------------- /backend/01 Add config/config/constants.js: -------------------------------------------------------------------------------- 1 | const { resolveFromRootPath } = require('./helpers'); 2 | 3 | exports.paths = { 4 | ts: resolveFromRootPath('./src/**/*.ts'), 5 | specs: resolveFromRootPath('./src/**/*.spec.ts'), 6 | output: resolveFromRootPath('./public'), 7 | tslintConfig: resolveFromRootPath('./tslint.json'), 8 | }; 9 | -------------------------------------------------------------------------------- /backend/01 Add config/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.resolve(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /backend/01 Add config/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "testRegex": "\\.spec\\.ts$", 4 | "moduleFileExtensions": [ 5 | "js", 6 | "json", 7 | "ts" 8 | ], 9 | "transform": { 10 | ".ts": "/node_modules/ts-jest/preprocessor.js" 11 | }, 12 | "restoreMocks": true 13 | } 14 | -------------------------------------------------------------------------------- /backend/01 Add config/src/env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | PORT: process.env.PORT, 3 | }; 4 | -------------------------------------------------------------------------------- /backend/01 Add config/src/routers/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './login'; 2 | -------------------------------------------------------------------------------- /backend/01 Add config/src/routers/login/controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | export const LoginController = () => ({ 4 | post: (req: Request, res: Response) => { 5 | isValidLogin(req.body.login, req.body.password) ? 6 | res.sendStatus(201) : 7 | res.sendStatus(401); 8 | }, 9 | }); 10 | 11 | const isValidLogin = (login: string, password: string) => ( 12 | login === 'admin' && 13 | password === 'test' 14 | ); 15 | -------------------------------------------------------------------------------- /backend/01 Add config/src/routers/login/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './router'; 2 | -------------------------------------------------------------------------------- /backend/01 Add config/src/routers/login/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { LoginController } from './controller'; 3 | 4 | export const loginRouter = () => { 5 | const router = Router(); 6 | const controller = LoginController(); 7 | router.route('/') 8 | .post(controller.post); 9 | 10 | return router; 11 | }; 12 | -------------------------------------------------------------------------------- /backend/01 Add config/src/sample.spec.ts: -------------------------------------------------------------------------------- 1 | describe('Sample tests', () => { 2 | it('should pass spec', () => { 3 | // Arrange 4 | 5 | // Act 6 | 7 | // Assert 8 | expect(true).toBeTruthy(); 9 | }); 10 | 11 | it('should fail spec', () => { 12 | // Arrange 13 | 14 | // Act 15 | 16 | // Assert 17 | expect(false).toBeTruthy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /backend/01 Add config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "sourceMap": true, 9 | "noLib": false, 10 | "outDir": "public" 11 | }, 12 | "compileOnSave": false, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /backend/02 Controllers/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=8081 3 | -------------------------------------------------------------------------------- /backend/02 Controllers/config/constants.js: -------------------------------------------------------------------------------- 1 | const { resolveFromRootPath } = require('./helpers'); 2 | 3 | exports.paths = { 4 | ts: resolveFromRootPath('./src/**/*.ts'), 5 | specs: resolveFromRootPath('./src/**/*.spec.ts'), 6 | output: resolveFromRootPath('./public'), 7 | tslintConfig: resolveFromRootPath('./tslint.json'), 8 | }; 9 | -------------------------------------------------------------------------------- /backend/02 Controllers/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.resolve(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /backend/02 Controllers/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "testRegex": "\\.spec\\.ts$", 4 | "moduleFileExtensions": [ 5 | "js", 6 | "json", 7 | "ts" 8 | ], 9 | "transform": { 10 | ".ts": "/node_modules/ts-jest/preprocessor.js" 11 | }, 12 | "restoreMocks": true 13 | } 14 | -------------------------------------------------------------------------------- /backend/02 Controllers/src/env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | PORT: process.env.PORT, 3 | }; 4 | -------------------------------------------------------------------------------- /backend/02 Controllers/src/routers/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './login'; 2 | -------------------------------------------------------------------------------- /backend/02 Controllers/src/routers/login/controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | export const LoginController = () => ({ 4 | post: (req: Request, res: Response) => { 5 | isValidLogin(req.body.login, req.body.password) ? 6 | res.sendStatus(201) : 7 | res.sendStatus(401); 8 | }, 9 | }); 10 | 11 | const isValidLogin = (login: string, password: string) => ( 12 | login === 'admin' && 13 | password === 'test' 14 | ); 15 | -------------------------------------------------------------------------------- /backend/02 Controllers/src/routers/login/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './router'; 2 | -------------------------------------------------------------------------------- /backend/02 Controllers/src/routers/login/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { LoginController } from './controller'; 3 | 4 | export const loginRouter = () => { 5 | const router = Router(); 6 | const controller = LoginController(); 7 | router.route('/') 8 | .post(controller.post); 9 | 10 | return router; 11 | }; 12 | -------------------------------------------------------------------------------- /backend/02 Controllers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "sourceMap": true, 9 | "noLib": false, 10 | "outDir": "public" 11 | }, 12 | "compileOnSave": false, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | PORT=8081 3 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/config/constants.js: -------------------------------------------------------------------------------- 1 | const { resolveFromRootPath } = require('./helpers'); 2 | 3 | exports.paths = { 4 | ts: resolveFromRootPath('./src/**/*.ts'), 5 | specs: resolveFromRootPath('./src/**/*.spec.ts'), 6 | output: resolveFromRootPath('./public'), 7 | tslintConfig: resolveFromRootPath('./tslint.json'), 8 | }; 9 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.resolve(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "testRegex": "\\.spec\\.ts$", 4 | "moduleFileExtensions": [ 5 | "js", 6 | "json", 7 | "ts" 8 | ], 9 | "transform": { 10 | ".ts": "/node_modules/ts-jest/preprocessor.js" 11 | }, 12 | "restoreMocks": true 13 | } 14 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/src/env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | PORT: process.env.PORT, 3 | }; 4 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/src/routers/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './login'; 2 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/src/routers/login/controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | 3 | export const LoginController = () => ({ 4 | post: (req: Request, res: Response) => { 5 | isValidLogin(req.body.login, req.body.password) ? 6 | res.sendStatus(201) : 7 | res.sendStatus(401); 8 | }, 9 | }); 10 | 11 | const isValidLogin = (login: string, password: string) => ( 12 | login === 'admin' && 13 | password === 'test' 14 | ); 15 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/src/routers/login/index.ts: -------------------------------------------------------------------------------- 1 | export { loginRouter } from './router'; 2 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/src/routers/login/router.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'express'; 2 | import { LoginController } from './controller'; 3 | 4 | export const loginRouter = () => { 5 | const router = Router(); 6 | const controller = LoginController(); 7 | router.route('/') 8 | .post(controller.post); 9 | 10 | return router; 11 | }; 12 | -------------------------------------------------------------------------------- /backend/03 Debugging Jest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "noImplicitAny": false, 8 | "sourceMap": true, 9 | "noLib": false, 10 | "outDir": "public" 11 | }, 12 | "compileOnSave": false, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ({ 5 | ...loginEntity, 6 | }); 7 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 |
10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 | 5 | 6 | 7 | Avatar 8 | 9 | 10 | Id 11 | 12 | 13 | Name 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapMemberListModelToVM = (members: model.Member[]): vm.Member[] => ( 5 | members.map(mapMemberModelToVM) 6 | ); 7 | 8 | const mapMemberModelToVM = (member: model.Member): vm.Member => ({ 9 | id: member.id, 10 | name: member.login, 11 | avatarUrl: member.avatar_url, 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | export interface State { 4 | 5 | } 6 | 7 | export const reducers = combineReducers({ 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/00 Boilerplate/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/01 Add config/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/01 Add config/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/01 Add config/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/01 Add config/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/01 Add config/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": ["/config/test/polyfills.js"], 5 | "restoreMocks": true 6 | } 7 | -------------------------------------------------------------------------------- /frontend/01 Add config/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ({ 5 | ...loginEntity, 6 | }); 7 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapMemberListModelToVM = (members: model.Member[]): vm.Member[] => ( 5 | members.map(mapMemberModelToVM) 6 | ); 7 | 8 | const mapMemberModelToVM = (member: model.Member): vm.Member => ({ 9 | id: member.id, 10 | name: member.login, 11 | avatarUrl: member.avatar_url, 12 | }); 13 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | export interface State { 4 | 5 | } 6 | 7 | export const reducers = combineReducers({ 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/sample.spec.ts: -------------------------------------------------------------------------------- 1 | describe('Sample tests', () => { 2 | it('should pass spec', () => { 3 | // Arrange 4 | 5 | // Act 6 | 7 | // Assert 8 | expect(true).toBeTruthy(); 9 | }); 10 | 11 | it('should fail spec', () => { 12 | // Arrange 13 | 14 | // Act 15 | 16 | // Assert 17 | expect(true).toBeFalsy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /frontend/01 Add config/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": ["/config/test/polyfills.js"], 5 | "restoreMocks": true 6 | } 7 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | export interface State { 4 | 5 | } 6 | 7 | export const reducers = combineReducers({ 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/02 Plain Vanilla/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": ["/config/test/polyfills.js"], 5 | "restoreMocks": true 6 | } 7 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | export interface State { 4 | 5 | } 6 | 7 | export const reducers = combineReducers({ 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/03 Debugging Jest/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": ["/config/test/polyfills.js"], 5 | "restoreMocks": true 6 | } 7 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | export interface State { 4 | 5 | } 6 | 7 | export const reducers = combineReducers({ 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/04 Redux Actions/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": ["/config/test/polyfills.js"], 5 | "restoreMocks": true 6 | } 7 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface Props { 4 | title: string; 5 | } 6 | 7 | export const Header = (props: Props) => ( 8 |
9 |

{props.title}

10 |
11 | ); 12 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/05 Redux Reducers/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/06 Components/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/06 Components/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/06 Components/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/06 Components/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/06 Components/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/06 Components/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/06 Components/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/06 Components/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/06 Components/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/06 Components/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/06 Components/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/06 Components/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/06 Components/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/06 Components/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/06 Components/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/components/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from '../viewModel'; 3 | import { Header } from './header'; 4 | import { Body } from './body'; 5 | 6 | interface Props { 7 | members: Member[]; 8 | } 9 | 10 | export const Table: React.StatelessComponent = (props) => ( 11 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
12 |
13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/06 Components/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/06 Components/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/06 Components/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/06 Components/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/06 Components/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/06 Components/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/07 Code Coverage/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/08 Reselect/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/08 Reselect/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/08 Reselect/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/08 Reselect/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/08 Reselect/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/08 Reselect/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/components/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from '../viewModel'; 3 | import { Header } from './header'; 4 | import { Body } from './body'; 5 | 6 | interface Props { 7 | members: Member[]; 8 | } 9 | 10 | export const Table: React.StatelessComponent = (props) => ( 11 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
12 |
13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/08 Reselect/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | install: 5 | - npm install 6 | script: 7 | - npm test 8 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/login/validations.ts: -------------------------------------------------------------------------------- 1 | import { createFormValidation, ValidationConstraints, Validators } from 'lc-form-validation'; 2 | 3 | const validationConstraints: ValidationConstraints = { 4 | fields: { 5 | login: [ 6 | { validator: Validators.required }, 7 | ], 8 | password: [ 9 | { validator: Validators.required }, 10 | ], 11 | }, 12 | }; 13 | 14 | export const validations = createFormValidation(validationConstraints); 15 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/components/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from '../viewModel'; 3 | import { Header } from './header'; 4 | import { Body } from './body'; 5 | 6 | interface Props { 7 | members: Member[]; 8 | } 9 | 10 | export const Table: React.StatelessComponent = (props) => ( 11 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
12 |
13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/09 Travis CI/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | install: 5 | - npm install 6 | script: 7 | - npm test 8 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | member: '/member/:name', 5 | }; 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/edit/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/edit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/edit/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapMemberModelToVM = (member: model.Member): vm.Member => ({ 5 | id: member.id, 6 | name: member.login, 7 | avatarUrl: member.avatar_url, 8 | }); 9 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/edit/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | 7 | export const createEmptyMember = (): Member => ({ 8 | id: -1, 9 | name: '', 10 | avatarUrl: '', 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | export * from './edit'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/components/row.scss: -------------------------------------------------------------------------------- 1 | .row { 2 | &:hover { 3 | cursor: pointer; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/00 Boilerplate/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": "3", 8 | } 9 | ] 10 | ], 11 | "plugins": ["react-hot-loader/babel"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | install: 5 | - npm install 6 | script: 7 | - npm test 8 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const rootPath = path.resolve(__dirname, '..'); 4 | 5 | exports.resolveFromRootPath = (...args) => path.join(rootPath, ...args); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/config/test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "rootDir": "../../", 3 | "preset": "ts-jest", 4 | "setupFiles": [ 5 | "/config/test/polyfills.js", 6 | "/config/test/setupTest.js" 7 | ], 8 | "restoreMocks": true, 9 | "snapshotSerializers": [ 10 | "enzyme-to-json/serializer" 11 | ], 12 | "moduleNameMapper": { 13 | "^.+\\.s?css$": "identity-obj-proxy" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/config/test/polyfills.js: -------------------------------------------------------------------------------- 1 | // Polyfill requestAnimationFrame required by React >=16.0.0 2 | require('raf/polyfill'); 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/config/test/setupTest.js: -------------------------------------------------------------------------------- 1 | const enzyme = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | // Setup enzyme's react adapter 5 | enzyme.configure({ adapter: new Adapter() }); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/app.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const App: React.StatelessComponent = (props) => ( 4 |
5 | {props.children} 6 |
7 | ); 8 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/appProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { hot } from 'react-hot-loader'; 3 | import { Provider } from 'react-redux'; 4 | import { store } from './store'; 5 | import { AppRouter } from './appRouter'; 6 | 7 | const AppProvider: React.StatelessComponent = (props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default hot(module)(AppProvider); 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/appRouter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Router } from 'react-router'; 3 | import { history } from './history'; 4 | import { Routes } from './routes'; 5 | 6 | export const AppRouter: React.StatelessComponent = () => ( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './input'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/__snapshots__/panel.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`commom/components/panel tests should render as expected when passing required properties 1`] = ` 4 |
7 | 10 | 11 |

12 | Test children component 13 |

14 |
15 |
16 | `; 17 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/body tests should render as expected when passing required properties 1`] = ` 4 |
    7 |
  • 10 |

    11 | Test children component 12 |

    13 |
  • 14 |
15 | `; 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/__snapshots__/header.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`common/components/panel/header tests should render as expected when passing required properties 1`] = ` 4 |
7 |

10 | test title 11 |

12 |
13 | `; 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Body: React.StatelessComponent = (props) => ( 4 |
    5 |
  • 6 | {props.children} 7 |
  • 8 |
9 | ); 10 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/header.scss: -------------------------------------------------------------------------------- 1 | :global(.card-header).header { 2 | background-color: #28A745; 3 | color: white; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const styles = require('./header.scss'); 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Header = (props: Props) => ( 9 |
10 |

{props.title}

11 |
12 | ); 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | export * from './header'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './panel'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/components/panel/panel.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Body, Header } from './components'; 3 | 4 | interface Props { 5 | title: string; 6 | } 7 | 8 | export const Panel: React.StatelessComponent = (props) => ( 9 |
10 |
11 | 12 | {props.children} 13 | 14 |
15 | ); 16 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/common/constants/routes/index.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | default: '/', 3 | members: '/members', 4 | member: '/member/:name', 5 | }; 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/history.ts: -------------------------------------------------------------------------------- 1 | import { createHashHistory } from 'history'; 2 | 3 | export const history = createHashHistory(); 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jest testing by sample 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import AppProvider from './appProvider'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | export * from './members'; 3 | export * from './reducers'; 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_LOGIN_ENTITY_FIELD: 'UPDATE_LOGIN_ENTITY_FIELD', 3 | UPDATE_LOGIN_FORM_ERRORS: 'UPDATE_LOGIN_FORM_ERRORS', 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/components/formProps.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity, LoginFormErrors } from '../viewModel'; 2 | 3 | export interface FormProps { 4 | loginEntity: LoginEntity; 5 | loginFormErrors: LoginFormErrors; 6 | updateField: (field: string, value: any) => void; 7 | doLogin: () => void; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | export * from './formProps'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapLoginEntityVMToModel = (loginEntity: vm.LoginEntity): model.LoginEntity => ( 5 | Boolean(loginEntity) ? 6 | { 7 | ...loginEntity, 8 | } : 9 | null 10 | ); 11 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Panel } from '../../common/components/panel'; 3 | import { Form, FormProps } from './components'; 4 | 5 | export const LoginPage: React.StatelessComponent = (props) => ( 6 | 7 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/login/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './login'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/edit/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './form'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/edit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/edit/mappers.ts: -------------------------------------------------------------------------------- 1 | import * as model from '../../../rest-api/model'; 2 | import * as vm from './viewModel'; 3 | 4 | export const mapMemberModelToVM = (member: model.Member): vm.Member => ({ 5 | id: member.id, 6 | name: member.login, 7 | avatarUrl: member.avatar_url, 8 | }); 9 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/edit/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | 7 | export const createEmptyMember = (): Member => ({ 8 | id: -1, 9 | name: '', 10 | avatarUrl: '', 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list'; 2 | export * from './edit'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/actions/actionIds.ts: -------------------------------------------------------------------------------- 1 | export const actionIds = { 2 | UPDATE_MEMBERS: 'UPDATE_MEMBERS', 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/components/header.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const Header: React.StatelessComponent = (props) => ( 4 |
5 | 6 | 9 | 12 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/components/row.scss: -------------------------------------------------------------------------------- 1 | .row { 2 | &:hover { 3 | cursor: pointer; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './pageContainer'; 2 | export * from './reducers'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Member } from './viewModel'; 3 | import { Table } from './components'; 4 | 5 | interface Props { 6 | members: Member[]; 7 | } 8 | 9 | export const MemberListPage: React.StatelessComponent = (props) => ( 10 | <> 11 |

Members

12 |
7 | Avatar 8 | 10 | Id 11 | 13 | Name 14 |
15 | 16 | ); 17 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './members'; 2 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/members/list/viewModel.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | name: string; 4 | avatarUrl: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/pages/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { loginReducer, LoginState } from './login'; 3 | import { membersReducer, MembersState } from './members/list'; 4 | 5 | export interface State { 6 | login: LoginState; 7 | members: MembersState; 8 | } 9 | 10 | export const reducers = combineReducers({ 11 | login: loginReducer, 12 | members: membersReducer, 13 | }); 14 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/rest-api/api/login.ts: -------------------------------------------------------------------------------- 1 | import { LoginEntity } from '../model'; 2 | 3 | export const login = (loginEntity: LoginEntity): Promise => ( 4 | isValidLogin(loginEntity) ? 5 | Promise.resolve() : 6 | Promise.reject('Not valid login or password') 7 | ); 8 | 9 | const isValidLogin = (loginEntity: LoginEntity) => ( 10 | loginEntity.login === 'admin' && 11 | loginEntity.password === 'test' 12 | ); 13 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/rest-api/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './loginEntity'; 2 | export * from './member'; 3 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/rest-api/model/loginEntity.ts: -------------------------------------------------------------------------------- 1 | export interface LoginEntity { 2 | login: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/rest-api/model/member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | id: number; 3 | login: string; 4 | avatar_url: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/10 Hooks/01 Testing hooks/src/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reduxThunk from 'redux-thunk'; 3 | import { reducers } from './pages'; 4 | 5 | const nonTypedWindow: any = window; 6 | const composeEnhancers = nonTypedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | export const store = createStore( 9 | reducers, 10 | composeEnhancers(applyMiddleware(reduxThunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/99 Readme Resources/03 Debugging Jest/01 Add launch.json file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/jest-testing-by-sample/af915af18f832fbc16691768a52be9e4eda5a103/frontend/99 Readme Resources/03 Debugging Jest/01 Add launch.json file.png -------------------------------------------------------------------------------- /frontend/99 Readme Resources/03 Debugging Jest/02 debugging_in_vscode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lemoncode/jest-testing-by-sample/af915af18f832fbc16691768a52be9e4eda5a103/frontend/99 Readme Resources/03 Debugging Jest/02 debugging_in_vscode.gif --------------------------------------------------------------------------------