From dd3e108bcafdaedfe63e8b8c0fa2d38c82ef608b Mon Sep 17 00:00:00 2001 From: Alejandro Lembke Barrientos Date: Thu, 12 Oct 2023 06:30:16 +0000 Subject: [PATCH] PR-610983: Creating the example project of a List Task App. --- .github/workflows/npm-test.yml | 44 +++++++++ README.md | 21 +--- package-lock.json | 95 +++++++++++++++++-- package.json | 10 +- src/frontend/actions/ListAction.ts | 65 +++++++++++++ src/frontend/actions/index.ts | 9 -- src/frontend/actions/testAction.ts | 25 ----- src/frontend/components/InitialComponent.scss | 38 -------- src/frontend/components/InitialComponent.tsx | 36 ------- src/frontend/components/List.tsx | 69 ++++++++++++++ src/frontend/components/OtherComponent.tsx | 18 ---- src/frontend/components/PrincipalRoutes.tsx | 2 +- .../components/__tests__/App.test.cy.tsx | 2 +- .../components/__tests__/App.test.tsx | 30 +++--- src/frontend/reducers/index.ts | 8 +- src/frontend/reducers/listReducer.ts | 48 ++++++++++ src/frontend/reducers/testReducer.ts | 20 ---- src/frontend/styles/global.scss | 2 +- src/routes/index.tsx | 12 +-- src/server/web/web.go | 2 +- 20 files changed, 353 insertions(+), 203 deletions(-) create mode 100644 .github/workflows/npm-test.yml create mode 100644 src/frontend/actions/ListAction.ts delete mode 100644 src/frontend/actions/index.ts delete mode 100644 src/frontend/actions/testAction.ts delete mode 100644 src/frontend/components/InitialComponent.scss delete mode 100644 src/frontend/components/InitialComponent.tsx create mode 100644 src/frontend/components/List.tsx delete mode 100644 src/frontend/components/OtherComponent.tsx create mode 100644 src/frontend/reducers/listReducer.ts delete mode 100644 src/frontend/reducers/testReducer.ts diff --git a/.github/workflows/npm-test.yml b/.github/workflows/npm-test.yml new file mode 100644 index 0000000..a35f593 --- /dev/null +++ b/.github/workflows/npm-test.yml @@ -0,0 +1,44 @@ +name: Testing package + +on: + pull_request: + branches: ['*'] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [16.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + steps: + - uses: actions/checkout@v3 + - name: Use Node.js 16 + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm test + cypress-run-component: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + # Install NPM dependencies, cache them correctly + # and run all Cypress tests + - name: Cypress run + uses: cypress-io/github-action@v5 # use the explicit version number + with: + component: true + test-build-package: + needs: [ test, cypress-run-component ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run build:frontend \ No newline at end of file diff --git a/README.md b/README.md index 8a7c4bf..dc8d896 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,15 @@ -# Create React SSR +# Test List App -This project aims to have a starter kit for creating a new React app with Server Side Rendering with a backend in go and tools that generally go along with it. - -It is not a project like create-react-app, create-react-app is used as a starter kit that handles all your scripts underneath, this is a project for developers who want more control over their application. - -Tech(Library or Framework) | Version | ---- | --- | -React (Render Library) | 18.2.0 -Redux (Global State Management) | 4.2.1 -React Router DOM (Routing) | 6.16.0 -Jest (Testing) | 29.7.0 -Cypress (E2E Testing) | 13.3.0 -Typescript | 5.2.2 +This project is an Example of a List of Tasks App. ## Setup -To create a new project run in the terminal: +To start the project you need to clone the repo: ``` -npx @aleleba/create-react-go-ssr app-name +git clone git@github.com:aleleba/test-list-app.git ``` Then run: ``` -cd app-name +cd test-list-app ``` You will need to create a new .env file at the root of the project for global config. This is an exaple of config. diff --git a/package-lock.json b/package-lock.json index 13f1432..6af55b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,20 @@ { - "name": "@aleleba/create-react-go-ssr", - "version": "1.0.3", + "name": "test-list-app", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@aleleba/create-react-go-ssr", - "version": "1.0.3", + "name": "test-list-app", + "version": "0.0.1", "license": "MIT", "dependencies": { "@babel/register": "^7.22.15", + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-brands-svg-icons": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", "dotenv": "^16.3.1", "express": "^4.18.2", "helmet": "^7.0.0", @@ -17,6 +22,7 @@ "ignore-styles": "^5.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-list-ui-library": "^1.0.2", "react-redux": "^8.1.3", "react-router-dom": "^6.16.0", "react-router-hash-link": "^2.4.3", @@ -36,9 +42,6 @@ "workbox-strategies": "^7.0.0", "workbox-streams": "^7.0.0" }, - "bin": { - "create-react-go-ssr": "bin/cli.js" - }, "devDependencies": { "@babel/core": "^7.23.0", "@babel/preset-env": "^7.22.20", @@ -2027,6 +2030,75 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", + "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", + "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz", + "integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", + "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", + "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "dev": true, @@ -13818,6 +13890,15 @@ "dev": true, "license": "MIT" }, + "node_modules/react-list-ui-library": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-list-ui-library/-/react-list-ui-library-1.0.2.tgz", + "integrity": "sha512-5f4ET0toaBPrDKdtuSLCiJ88y7TmWmpgXlvK6BiRAcXwFLOaYL8VJgGIhSL+GBny5HG88Rg/PIYeNS6AJeMmYA==", + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, "node_modules/react-redux": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", diff --git a/package.json b/package.json index 68aa9c0..58cce53 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "test-list-app", - "version": "0.0.1", + "version": "1.0.0", "description": "Starter Kit of server side render of react with backend in go", - "scripts": { + "scripts": { "start": "cd build/server && ./react-server", "start-frontend:dev": "webpack watch --config webpack.config.ts", "start-server:dev": "cd src/server && go run main.go", @@ -38,6 +38,11 @@ "homepage": "https://github.com/aleleba/create-react-go-ssr#readme", "dependencies": { "@babel/register": "^7.22.15", + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-brands-svg-icons": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", "dotenv": "^16.3.1", "express": "^4.18.2", "helmet": "^7.0.0", @@ -45,6 +50,7 @@ "ignore-styles": "^5.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-list-ui-library": "^1.0.2", "react-redux": "^8.1.3", "react-router-dom": "^6.16.0", "react-router-hash-link": "^2.4.3", diff --git a/src/frontend/actions/ListAction.ts b/src/frontend/actions/ListAction.ts new file mode 100644 index 0000000..6e487dc --- /dev/null +++ b/src/frontend/actions/ListAction.ts @@ -0,0 +1,65 @@ +/* List */ +export type TList = { + list: TItem[] +} + +export interface IListPayload { + index?: number + item?: TItem +} + +export type TItem = { + name: string, + status: Status +} + +export enum Status { + TODO = 'TODO', + DONE = 'DONE' +} + +export interface IAddItemToList { + type: ActionTypesList.AddItem + payload: IListPayload +} + +export interface IChangeStatus { + type: ActionTypesList.ChangeStatus + payload: IListPayload +} + +export interface IDeleteItemToList { + type: ActionTypesList.DeleteItem + payload: IListPayload +} + +export enum ActionTypesList { + AddItem = 'ADD_ITEM', + ChangeStatus = 'CHANGE_STATUS', + DeleteItem = 'DELETE_ITEM' +} + +export type TListAction = { + type: ActionTypesList + payload: IListPayload +} + +const addItem = (payload: IAddItemToList) => ({ + type: ActionTypesList.AddItem, + payload +}); + +const changeStatus = (payload: IChangeStatus) => ({ + type: ActionTypesList.ChangeStatus, + payload +}); + +const listActions = { + addItem, + changeStatus +}; +/* List */ + + + +export default listActions; diff --git a/src/frontend/actions/index.ts b/src/frontend/actions/index.ts deleted file mode 100644 index b95c148..0000000 --- a/src/frontend/actions/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import test, { TTest } from './testAction'; - -export type TAction = TTest - -const actions = { - test -} - -export default actions \ No newline at end of file diff --git a/src/frontend/actions/testAction.ts b/src/frontend/actions/testAction.ts deleted file mode 100644 index 30593bf..0000000 --- a/src/frontend/actions/testAction.ts +++ /dev/null @@ -1,25 +0,0 @@ -export enum ActionTypesTest { - ChangeHello = 'CHANGE_HELLO' -} - -export interface IChangeHello { - type: ActionTypesTest.ChangeHello - payload: IChangeHelloPayload -} - -export interface IChangeHelloPayload { - hello: any | undefined -} - -export type TTest = IChangeHello - -const changeHello = (payload: string) => ({ - type: ActionTypesTest.ChangeHello, - payload -}) - -const actions = { - changeHello -} - -export default actions \ No newline at end of file diff --git a/src/frontend/components/InitialComponent.scss b/src/frontend/components/InitialComponent.scss deleted file mode 100644 index a988b49..0000000 --- a/src/frontend/components/InitialComponent.scss +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } -} \ No newline at end of file diff --git a/src/frontend/components/InitialComponent.tsx b/src/frontend/components/InitialComponent.tsx deleted file mode 100644 index 18aa7c1..0000000 --- a/src/frontend/components/InitialComponent.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import './InitialComponent.scss'; -import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; - -const InitialComponent = ({ hello }: { hello: string }) => { - - return( -
-
- logo -

This is the text from the store of redux: {hello}

-

- Edit src/frontend/InitialComponent.jsx and save to reload. -

- - Learn React - - Other Component -
-
- ); -}; - -const mapStateToProps = (state) => { - return { - hello: state.testReducer.hello - }; -}; - -export default connect(mapStateToProps)(InitialComponent); diff --git a/src/frontend/components/List.tsx b/src/frontend/components/List.tsx new file mode 100644 index 0000000..14af20a --- /dev/null +++ b/src/frontend/components/List.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { connect } from 'react-redux'; +import { TItem, ActionTypesList, Status } from '../actions/ListAction'; +import 'react-list-ui-library/dist/index.css'; +import { ContainerList, List } from 'react-list-ui-library'; + + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ListComponent = ({ list, addItem, changeStatus, deleteItem }: {list: TItem[], addItem: (item: TItem) => any, changeStatus: (index: number) => any, deleteItem: (index: number) => any }) => { + + const [itemName, setItemName] = useState(''); + + const onClickAddItem = () => { + const item: TItem = { + name: itemName, + status: Status.TODO + }; + if(itemName !== ''){ + addItem(item); + setItemName(''); + }else{ + alert('Please, insert a name'); + } + }; + + const onChangeInput = (e) => { + setItemName(e.target.value); + }; + + const handleChangeState = (index) => { + changeStatus(index); + }; + + const onClickRemoveItem = (index) => { + deleteItem(index); + }; + + return( +
+ + + +
+ ); +}; + +const mapStateToProps = (state) => { + return { + list: state.listReducer.list + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + addItem: (item: TItem) => dispatch({ type: ActionTypesList.AddItem, payload: { item: item } }), + changeStatus: (index: number) => dispatch({ type: ActionTypesList.ChangeStatus, payload: { index: index } }), + deleteItem: (index: number) => dispatch({ type: ActionTypesList.DeleteItem, payload: { index: index } }) + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(ListComponent); diff --git a/src/frontend/components/OtherComponent.tsx b/src/frontend/components/OtherComponent.tsx deleted file mode 100644 index b249292..0000000 --- a/src/frontend/components/OtherComponent.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -// import logo from '../logo.svg'; -import './InitialComponent.scss'; -import { Link } from 'react-router-dom'; - -const OtherComponent = () => ( -
-
- logo -

- Edit src/frontend/OtherComponent.jsx and save to reload. -

- Initial Component -
-
-); - -export default OtherComponent; diff --git a/src/frontend/components/PrincipalRoutes.tsx b/src/frontend/components/PrincipalRoutes.tsx index 73bf9ef..6e0d62b 100644 --- a/src/frontend/components/PrincipalRoutes.tsx +++ b/src/frontend/components/PrincipalRoutes.tsx @@ -4,7 +4,7 @@ import { useRoutes } from 'react-router-dom'; import routes from '../../routes'; const PrincipalRoutes = () => { - let element = useRoutes(routes); + const element = useRoutes(routes); return element; }; diff --git a/src/frontend/components/__tests__/App.test.cy.tsx b/src/frontend/components/__tests__/App.test.cy.tsx index face8a4..79d85ec 100644 --- a/src/frontend/components/__tests__/App.test.cy.tsx +++ b/src/frontend/components/__tests__/App.test.cy.tsx @@ -11,6 +11,6 @@ describe('Testing Card Component', () => { ); }); it('Show Text', () => { - cy.get('p').contains('Edit src/frontend/InitialComponent.jsx and save to reload.'); + cy.get('div').contains('List App'); }); }); diff --git a/src/frontend/components/__tests__/App.test.tsx b/src/frontend/components/__tests__/App.test.tsx index 6e03a0d..364494c 100644 --- a/src/frontend/components/__tests__/App.test.tsx +++ b/src/frontend/components/__tests__/App.test.tsx @@ -4,20 +4,20 @@ import { ProviderMock } from '@mocks'; import App from '@components/App'; describe(' Component', () => { - beforeEach(() => { - fetchMock.resetMocks(); - }); + beforeEach(() => { + fetchMock.resetMocks(); + }); - it('Should render root Component', async () => { - fetchMock.mockResponseOnce(JSON.stringify({ - //First Data Fetch - data: 'data' - })); + it('Should render root Component', async () => { + fetchMock.mockResponseOnce(JSON.stringify({ + //First Data Fetch + data: 'data' + })); - render( - - - - ) - }) -}) \ No newline at end of file + render( + + + + ); + }); +}); diff --git a/src/frontend/reducers/index.ts b/src/frontend/reducers/index.ts index 29d9721..6798048 100644 --- a/src/frontend/reducers/index.ts +++ b/src/frontend/reducers/index.ts @@ -1,14 +1,14 @@ import { combineReducers } from 'redux'; -import testReducer from './testReducer'; -import { IChangeHelloPayload } from '../actions/testAction'; +import listReducer from './listReducer'; +import { TList } from '../actions/ListAction'; export interface IInitialState { - testReducer?: IChangeHelloPayload | undefined + listReducer?: TList | undefined } const rootReducer = combineReducers({ // Here comes the reducers - testReducer + listReducer }); export default rootReducer; diff --git a/src/frontend/reducers/listReducer.ts b/src/frontend/reducers/listReducer.ts new file mode 100644 index 0000000..90690da --- /dev/null +++ b/src/frontend/reducers/listReducer.ts @@ -0,0 +1,48 @@ +import { ActionTypesList, TListAction, TItem, Status } from '../actions/ListAction'; + +const initialState = { + list: [] +}; + +const listReducer = (state = initialState, action: TListAction) => { + switch (action.type){ + case ActionTypesList.AddItem: { + const newList = [ + ...state.list, + action.payload.item + ]; + return { + list: newList + }; + } + case ActionTypesList.ChangeStatus: { + const itemList = state.list[action.payload.index as number] as TItem; + const actualStatus = itemList.status; + const newItemList = { + ...itemList, + status: actualStatus === Status.TODO ? Status.DONE : Status.TODO + }; + const newList = [ + ...state.list.slice(0, action.payload.index as number), + newItemList, + ...state.list.slice(action.payload.index as number + 1) + ]; + return { + list: newList + }; + } + case ActionTypesList.DeleteItem: { + const newList = [ + ...state.list.slice(0, action.payload.index as number), + ...state.list.slice(action.payload.index as number + 1) + ]; + return { + list: newList + }; + } + default: + return state; + } +}; + +export default listReducer; diff --git a/src/frontend/reducers/testReducer.ts b/src/frontend/reducers/testReducer.ts deleted file mode 100644 index 6c27a59..0000000 --- a/src/frontend/reducers/testReducer.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TAction } from '../actions'; - -const initialState = { - hello: 'world' -}; - -const testReducer = (state = initialState, action: TAction) => { - switch (action.type){ - case 'CHANGE_HELLO': { - const newHello = action.payload.hello; - return { - hello: newHello - }; - } - default: - return state; - } -}; - -export default testReducer; diff --git a/src/frontend/styles/global.scss b/src/frontend/styles/global.scss index fda9030..88c9323 100644 --- a/src/frontend/styles/global.scss +++ b/src/frontend/styles/global.scss @@ -1,4 +1,4 @@ -$base-color: #282c34; +$base-color: #FFFFFF; body { background-color: $base-color; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 0bac04a..ce51341 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,15 +1,9 @@ import React from 'react'; -import InitialComponent from '../frontend/components/InitialComponent'; -import OtherComponent from '../frontend/components/OtherComponent'; - -const OTHER_COMPONENT = { - path: '/other-component', - element: -}; +import List from '../frontend/components/List'; const INITIAL_COMPONENT = { path: '/', - element: , + element: , }; -export default [ INITIAL_COMPONENT, OTHER_COMPONENT ]; +export default [ INITIAL_COMPONENT ]; diff --git a/src/server/web/web.go b/src/server/web/web.go index 16888c7..cea7de3 100644 --- a/src/server/web/web.go +++ b/src/server/web/web.go @@ -43,7 +43,7 @@ func RegisterHandlers(e *echo.Echo, paths []string) { - + App