Skip to content

Commit 65c20c8

Browse files
committed
Update and cleanup
1 parent c171ca3 commit 65c20c8

40 files changed

+319
-867
lines changed

.eslintrc.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@ const baseRestrictedImports = {
3232
}
3333

3434
module.exports = {
35+
root: true,
3536
plugins: ["@typescript-eslint", "react", "react-hooks", "jsx-a11y", "jest", "testing-library", "import"],
3637
env: {
3738
amd: true,
3839
es2022: true,
3940
browser: true,
41+
node: true,
4042
},
4143
reportUnusedDisableDirectives: true,
4244
parserOptions: {
43-
parser: "@babel/eslint-parser",
4445
ecmaVersion: "latest",
4546
ecmaFeatures: {
4647
jsx: true,
@@ -249,7 +250,11 @@ module.exports = {
249250
},
250251
{
251252
// JSX A11y - This plugin is being extended because there's an extensive amount of custom options automatically configured. - https://github.com/jsx-eslint/eslint-plugin-jsx-a11y
252-
extends: ["plugin:jsx-a11y/recommended"],
253+
extends: [
254+
"plugin:jsx-a11y/recommended",
255+
"plugin:react/recommended",
256+
"eslint:recommended",
257+
],
253258
files: ["*.jsx", "*.tsx"],
254259
rules: {
255260
// React - https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules
@@ -426,11 +431,12 @@ module.exports = {
426431
"no-class-assign": "error",
427432
"no-compare-neg-zero": "error",
428433
"no-cond-assign": "error",
434+
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
429435
"no-const-assign": "error",
430436
"no-constant-condition": "error",
431437
"no-constructor-return": "error",
432438
"no-control-regex": "error",
433-
"no-debugger": "warn",
439+
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
434440
"no-dupe-args": "error",
435441
"no-dupe-class-members": "error",
436442
"no-dupe-else-if": "error",

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ yarn.lock
3535
tmp/*
3636
!tmp/readme.md
3737
webpack-report
38+
**/__snapshots__/

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ React starter-kit project
88
* eslint
99
* react-redux with seamless-immutable state
1010
* reduxsauce
11-
* react-router
11+
* react-router-dom
1212
* axios
1313
* jest
1414
* i18n-react
@@ -23,5 +23,8 @@ React starter-kit project
2323
3. visit [http://localhost:3000](http://localhost:3000/)
2424

2525
## Build
26-
1. npm build or yarn build
26+
1. npm run build or yarn build
2727
2. go to "dist" folder, located in the root directory
28+
29+
## Test (jest)
30+
npm test or yarn test

jest.config.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
/* eslint-disable no-undef */
22

33
module.exports = {
4-
rootDir: 'src',
4+
rootDir: "src",
55
verbose: true,
6-
}
6+
transform: {
7+
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest"
8+
},
9+
moduleNameMapper:{
10+
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
11+
},
12+
testEnvironment: "jsdom"
13+
}

package.json

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "react-skeleton-app",
2+
"name": "react-redux-starter-kit",
33
"version": "0.0.1",
44
"description": "React starter-kit project",
55
"author": "Václav Daněk",
@@ -16,74 +16,77 @@
1616
"type-check": "tsc"
1717
},
1818
"dependencies": {
19-
"@fortawesome/fontawesome-svg-core": "^6.4.0",
20-
"@fortawesome/free-regular-svg-icons": "^6.4.0",
21-
"@fortawesome/free-solid-svg-icons": "^6.4.0",
22-
"@fortawesome/react-fontawesome": "^0.2.0",
23-
"@redux-devtools/extension": "^3.2.5",
24-
"axios": "^1.3.6",
19+
"@fortawesome/fontawesome-svg-core": "^6.7.2",
20+
"@fortawesome/free-regular-svg-icons": "^6.7.2",
21+
"@fortawesome/free-solid-svg-icons": "^6.7.2",
22+
"@fortawesome/react-fontawesome": "^0.2.2",
23+
"@redux-devtools/extension": "^3.3.0",
24+
"axios": "^1.8.4",
2525
"bootstrap": "5.3.3",
26-
"classnames": "^2.2.5",
27-
"i18n-react": "^0.7.0",
28-
"js-utils": "https://github.com/VaclavDanek/js-utils.git",
26+
"classnames": "^2.5.1",
27+
"i18next": "^24.2.3",
28+
"i18next-browser-languagedetector": "^8.0.4",
29+
"js-utils": "github:VaclavDanek/js-utils",
2930
"loaders.css": "^0.1.2",
30-
"moment": "^2.22.1",
31+
"moment": "^2.30.1",
3132
"node-sass": "^9.0.0",
32-
"react": "18.3.1",
33-
"react-device-detect": "2.2.3",
34-
"react-dom": "18.3.1",
33+
"react": "^18.3.1",
34+
"react-device-detect": "^2.2.3",
35+
"react-dom": "^18.3.1",
36+
"react-i18next": "^15.4.1",
3537
"react-loaders": "^3.0.1",
36-
"react-redux": "^9.1.2",
37-
"react-router-dom": "^6.10.0",
38-
"reactstrap": "^9.1.9",
39-
"redbox-react": "^1.6.0",
38+
"react-redux": "^9.2.0",
39+
"react-router-dom": "^6.30.0",
40+
"reactstrap": "^9.2.3",
4041
"redux": "^5.0.1",
4142
"redux-observable": "^3.0.0-rc.2",
4243
"redux-thunk": "^3.1.0",
43-
"reduxsauce": "^1.2.1",
44-
"rxjs": "^7.8.0",
44+
"reduxsauce": "^1.3.0",
45+
"rxjs": "^7.8.2",
4546
"seamless-immutable": "^7.1.4"
4647
},
4748
"devDependencies": {
48-
"@babel/core": "^7.21.4",
49-
"@babel/eslint-parser": "^7.21.3",
50-
"@babel/preset-env": "^7.21.4",
51-
"@babel/preset-react": "^7.0.0",
52-
"@babel/preset-typescript": "^7.21.4",
53-
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
54-
"@types/enzyme": "^3.10.13",
55-
"@types/jest": "^29.5.1",
56-
"@types/react-dom": "^18.2.1",
57-
"@types/seamless-immutable": "^7.1.16",
58-
"@types/webpack-env": "^1.18.0",
59-
"@typescript-eslint/eslint-plugin": "^8.9.0",
60-
"@typescript-eslint/parser": "^8.9.0",
61-
"autoprefixer": "^10.4.13",
62-
"babel-loader": "^9.1.2",
49+
"@babel/core": "^7.26.10",
50+
"@babel/eslint-parser": "^7.27.0",
51+
"@babel/preset-env": "^7.26.9",
52+
"@babel/preset-react": "^7.26.3",
53+
"@babel/preset-typescript": "^7.27.0",
54+
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.16",
55+
"@types/jest": "^29.5.14",
56+
"@types/react-dom": "^18.3.6",
57+
"@types/seamless-immutable": "^7.1.19",
58+
"@types/webpack-env": "^1.18.8",
59+
"@typescript-eslint/eslint-plugin": "^8.29.1",
60+
"@typescript-eslint/parser": "^8.29.1",
61+
"autoprefixer": "^10.4.21",
62+
"babel-loader": "^9.2.1",
6363
"copy-webpack-plugin": "^12.0.2",
64-
"css-loader": "7.1.2",
65-
"eslint": "^9.12.0",
66-
"eslint-plugin-import": "^2.27.5",
67-
"eslint-plugin-jest": "^28.8.3",
68-
"eslint-plugin-jsx-a11y": "^6.7.1",
69-
"eslint-plugin-react": "^7.32.2",
70-
"eslint-plugin-react-hooks": "^5.0.0",
71-
"eslint-plugin-testing-library": "^6.3.0",
72-
"html-webpack-plugin": "^5.5.1",
73-
"jest": "^29.4.3",
74-
"mini-css-extract-plugin": "^2.7.2",
64+
"css-loader": "^7.1.2",
65+
"eslint": "^9.24.0",
66+
"eslint-plugin-import": "^2.31.0",
67+
"eslint-plugin-jest": "^28.11.0",
68+
"eslint-plugin-jsx-a11y": "^6.10.2",
69+
"eslint-plugin-react": "^7.37.5",
70+
"eslint-plugin-react-hooks": "^5.2.0",
71+
"eslint-plugin-testing-library": "^6.5.0",
72+
"html-webpack-plugin": "^5.6.3",
73+
"identity-obj-proxy": "^3.0.0",
74+
"jest": "^29.7.0",
75+
"jest-environment-jsdom": "^29.7.0",
76+
"mini-css-extract-plugin": "^2.9.2",
7577
"postcss-loader": "^8.1.1",
76-
"prettier": "^3.3.3",
77-
"react-refresh": "^0.14.0",
78-
"react-test-renderer": "^18.2.0",
79-
"sass-loader": "^16.0.2",
80-
"style-loader": "4.0.0",
81-
"typescript": "^5.0.4",
82-
"uglifyjs-webpack-plugin": "^2.2.0",
83-
"webpack": "^5.80.0",
84-
"webpack-bundle-analyzer": "^4.8.0",
85-
"webpack-cli": "^5.0.2",
86-
"webpack-dashboard": "^3.3.7",
87-
"webpack-dev-server": "^5.1.0"
78+
"prettier": "^3.5.3",
79+
"react-refresh": "^0.14.2",
80+
"react-test-renderer": "^18.3.1",
81+
"sass-loader": "^16.0.5",
82+
"style-loader": "^4.0.0",
83+
"terser-webpack-plugin": "^5.3.14",
84+
"ts-jest": "^29.3.2",
85+
"typescript": "^5.8.3",
86+
"webpack": "^5.99.5",
87+
"webpack-bundle-analyzer": "^4.10.2",
88+
"webpack-cli": "^5.1.4",
89+
"webpack-dashboard": "^3.3.8",
90+
"webpack-dev-server": "^5.2.1"
8891
}
8992
}
Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,55 @@
1-
import I18n from 'i18n-react'
21
import { Component } from 'react'
32
import { connect } from 'react-redux'
3+
import { compose } from 'redux'
44
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
66
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
7-
import * as config from '../config'
7+
import { withTranslation } from 'react-i18next'
8+
import * as config from './config'
89

910
// pages
10-
import { HomePage } from '../pages'
11+
import { HomePage } from './pages'
1112

1213
// components
13-
import { Loader, Modals } from '../components'
14+
import { Loader, Modals } from './components'
1415

1516
// redux
16-
import { generalActions } from '../redux/generalRedux'
17+
import { generalActions } from './redux/generalRedux'
1718

1819
// types
19-
import type { State } from '../store/reducers'
20-
import type { CustomErrorEvent } from '../types/errorTypes'
21-
import type { ModalKey } from '../components/Modals'
20+
import type { ElementType } from 'react'
21+
import type { WithTranslation } from 'react-i18next'
22+
import type { State } from './store/reducers'
23+
import type { CustomErrorEvent } from './types/errorTypes'
24+
import type { ModalKey } from './components/Modals'
2225

2326
// styles
2427
import 'bootstrap/scss/bootstrap.scss'
25-
import '../styles/main.scss'
26-
import '../styles/mobile.scss'
28+
import './styles/main.scss'
2729

28-
type RootContainerProps = Readonly<ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps>
30+
type AppContainerProps = Readonly<WithTranslation & ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps>
2931

30-
interface RootContainerState {
32+
interface AppContainerState {
3133
hasError?: boolean;
3234
}
3335

34-
class RootContainer extends Component<RootContainerProps, RootContainerState> {
35-
state: RootContainerState = {}
36+
class AppContainer extends Component<AppContainerProps, AppContainerState> {
37+
state: AppContainerState = {}
3638

37-
constructor(props: RootContainerProps) {
39+
constructor(props: AppContainerProps) {
3840
super(props)
3941
window.addEventListener('error', this.handleOnError)
4042
}
4143

4244
static getDerivedStateFromProps(
43-
nextProps: RootContainerProps,
44-
prevState: RootContainerState, // eslint-disable-line @typescript-eslint/no-unused-vars
45-
): Partial<RootContainerState> | null {
46-
const { redirectUrl, setRedirectUrl } = nextProps
47-
if (redirectUrl) { // reset after every redirect
48-
setRedirectUrl('')
49-
}
45+
nextProps: AppContainerProps,
46+
prevState: AppContainerState, // eslint-disable-line @typescript-eslint/no-unused-vars
47+
): Partial<AppContainerState> | null {
5048
return null
5149
}
5250

53-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
54-
static getDerivedStateFromError(error: Error): Partial<RootContainerState> {
51+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
52+
static getDerivedStateFromError(error: Error): Partial<AppContainerState> {
5553
return { hasError: true }
5654
}
5755

@@ -70,26 +68,18 @@ class RootContainer extends Component<RootContainerProps, RootContainerState> {
7068
}
7169

7270
handleOnError = (errorEvent?: ErrorEvent | CustomErrorEvent): void => {
73-
const { addAlert, onActionFailure, onStopFetching } = this.props
71+
const { addAlert, onActionFailure, onStopFetching, t } = this.props
7472

7573
onStopFetching() // just for sure...
7674
if (errorEvent && !config.ignoredErrorEventMessages.includes(errorEvent.message)) {
7775
onActionFailure(errorEvent)
7876
if (process.env.NODE_ENV === 'production' && config.showErrorAlert) {
79-
addAlert({ message: I18n.translate('alerts.onGlobalError') as string, type: 'warning' })
77+
addAlert({ message: t('alerts.onGlobalError') as string, type: 'warning' })
8078
}
8179
}
8280
}
8381

84-
handleOnRedirect = (path: string): void => {
85-
this.props.setRedirectUrl(path)
86-
}
87-
88-
handleOnReload = (): void => {
89-
window.location.reload()
90-
}
91-
92-
handleOnCloseAlert = (index: number): void => {
82+
handleCloseAlert = (index: number): void => {
9383
this.props.removeAlert(index)
9484
}
9585

@@ -98,11 +88,11 @@ class RootContainer extends Component<RootContainerProps, RootContainerState> {
9888
}
9989

10090
render(): JSX.Element {
101-
const { handleOnCloseAlert, handleToggleModal } = this
102-
const { alerts, fetching, modals, redirectUrl } = this.props
91+
const { handleCloseAlert, handleToggleModal } = this
92+
const { alerts, fetching, modals } = this.props
10393

10494
return <>
105-
<BrowserRouter basename={config.basename}>
95+
<BrowserRouter basename={process.env.NODE_ENV === 'production' ? config.basename : undefined}>
10696
<Routes>
10797
<Route
10898
path='/'
@@ -117,12 +107,11 @@ class RootContainer extends Component<RootContainerProps, RootContainerState> {
117107
}
118108
/>
119109
</Routes>
120-
{redirectUrl && <Navigate to={redirectUrl} />}
121110
</BrowserRouter>
122111
<Modals
123112
alerts={alerts}
124113
modals={modals}
125-
onCloseAlert={handleOnCloseAlert}
114+
onCloseAlert={handleCloseAlert}
126115
onToggleModal={handleToggleModal}
127116
/>
128117
<Loader active={fetching > 0} />
@@ -140,8 +129,10 @@ const mapDispatchToProps = {
140129
onActionFailure: generalActions.onActionFailure,
141130
onStopFetching: generalActions.onStopFetching,
142131
removeAlert: generalActions.removeAlert,
143-
setRedirectUrl: generalActions.setRedirectUrl,
144132
toggleModal: generalActions.toggleModal,
145133
}
146134

147-
export default connect(mapStateToProps, mapDispatchToProps)(RootContainer)
135+
export default compose<ElementType>(
136+
withTranslation(),
137+
connect(mapStateToProps, mapDispatchToProps),
138+
)(AppContainer)

0 commit comments

Comments
 (0)