mirror of
https://github.com/aleleba/test-list-app.git
synced 2025-09-15 02:56:56 -06:00
PR-610983: Creating the example project of a List Task App.
This commit is contained in:
65
src/frontend/actions/ListAction.ts
Normal file
65
src/frontend/actions/ListAction.ts
Normal file
@ -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;
|
@ -1,9 +0,0 @@
|
||||
import test, { TTest } from './testAction';
|
||||
|
||||
export type TAction = TTest
|
||||
|
||||
const actions = {
|
||||
test
|
||||
}
|
||||
|
||||
export default actions
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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(
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src="assets/img/logo.svg" className="App-logo" alt="logo" />
|
||||
<p>This is the text from the store of redux: <strong>{hello}</strong></p>
|
||||
<p>
|
||||
Edit <code>src/frontend/InitialComponent.jsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
<Link className="App-link" to="/other-component">Other Component</Link>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
hello: state.testReducer.hello
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(InitialComponent);
|
69
src/frontend/components/List.tsx
Normal file
69
src/frontend/components/List.tsx
Normal file
@ -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<string>('');
|
||||
|
||||
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(
|
||||
<div className="App">
|
||||
<ContainerList title='List App'>
|
||||
<List
|
||||
list={list}
|
||||
valueInput={itemName}
|
||||
placeholderInput='Insert a name of a new item'
|
||||
onChangeInput={onChangeInput}
|
||||
onClickAddItem={onClickAddItem}
|
||||
onClickRemoveItem={onClickRemoveItem}
|
||||
handleChangeState={handleChangeState}
|
||||
/>
|
||||
</ContainerList>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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);
|
@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
// import logo from '../logo.svg';
|
||||
import './InitialComponent.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const OtherComponent = () => (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src="assets/img/logo.svg" className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/frontend/OtherComponent.jsx</code> and save to reload.
|
||||
</p>
|
||||
<Link className="App-link" to="/">Initial Component</Link>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default OtherComponent;
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -4,20 +4,20 @@ import { ProviderMock } from '@mocks';
|
||||
import App from '@components/App';
|
||||
|
||||
describe('<App/> Component', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
beforeEach(() => {
|
||||
fetchMock.resetMocks();
|
||||
});
|
||||
|
||||
it('Should render root <App /> Component', async () => {
|
||||
fetchMock.mockResponseOnce(JSON.stringify({
|
||||
//First Data Fetch
|
||||
data: 'data'
|
||||
}));
|
||||
it('Should render root <App /> Component', async () => {
|
||||
fetchMock.mockResponseOnce(JSON.stringify({
|
||||
//First Data Fetch
|
||||
data: 'data'
|
||||
}));
|
||||
|
||||
render(
|
||||
<ProviderMock>
|
||||
<App />
|
||||
</ProviderMock>
|
||||
)
|
||||
})
|
||||
})
|
||||
render(
|
||||
<ProviderMock>
|
||||
<App />
|
||||
</ProviderMock>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
48
src/frontend/reducers/listReducer.ts
Normal file
48
src/frontend/reducers/listReducer.ts
Normal file
@ -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;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
$base-color: #282c34;
|
||||
$base-color: #FFFFFF;
|
||||
|
||||
body {
|
||||
background-color: $base-color;
|
||||
|
@ -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: <OtherComponent />
|
||||
};
|
||||
import List from '../frontend/components/List';
|
||||
|
||||
const INITIAL_COMPONENT = {
|
||||
path: '/',
|
||||
element: <InitialComponent />,
|
||||
element: <List />,
|
||||
};
|
||||
|
||||
export default [ INITIAL_COMPONENT, OTHER_COMPONENT ];
|
||||
export default [ INITIAL_COMPONENT ];
|
||||
|
@ -43,7 +43,7 @@ func RegisterHandlers(e *echo.Echo, paths []string) {
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!-- ${manifestJson} -->
|
||||
<link href="assets/frontend.css" rel="stylesheet" type="text/css"></link>
|
||||
|
||||
<link href="assets/vendors.css" rel="stylesheet" type="text/css"></link>
|
||||
<title>App</title>
|
||||
</head>
|
||||
<body>
|
||||
|
Reference in New Issue
Block a user