Skip to content

Commit

Permalink
React 18 & Patternfly 5 Upgrades
Browse files Browse the repository at this point in the history
- Upgrade Patternfly from v4 to v5
- Upgrade React from 17 to 18
- Upgrade react router from v5 to v6
- Upgrade packages & replace some unrelevant
- Fix stylings
- Make linter happy
- Make tests work
- Format code for better visibility
- Create elementWrapper helper component to make router v6 work
- Fix some warnings
- Some bugfixes to already existing issues
- Minor backend issue fix
- Make filtering work for ibutsu projects on backend side
- Remove unused components
  • Loading branch information
LightOfHeaven1994 committed Jun 6, 2024
1 parent be116c3 commit 0848b65
Show file tree
Hide file tree
Showing 61 changed files with 2,802 additions and 1,294 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ target/

# Editors
.vscode
.idea/

# NodeJS stuff
node_modules
package-lock.json
yarn.lock

# Virtual environment
.ibutsu-env
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ repos:
- eslint-plugin-react
- eslint-plugin-import
- "@babel/eslint-parser"
- "@babel/plugin-proposal-class-properties"
- "@babel/plugin-proposal-private-methods"
- "@babel/plugin-transform-class-properties"
- "@babel/plugin-transform-private-methods"
- "@babel/plugin-syntax-jsx"
- "@babel/preset-flow"
- "@babel/core"
Expand Down
2 changes: 1 addition & 1 deletion backend/ibutsu_server/controllers/login_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def register(email=None, password=None):
user = User(
email=details["email"], password=details["password"], activation_code=activation_code
)
user_exists = User.query.filter_by(email=user.email)
user_exists = User.query.filter_by(email=user.email).first()
if user_exists:
return f"The user with email {user.email} already exists", 400
session.add(user)
Expand Down
8 changes: 8 additions & 0 deletions backend/ibutsu_server/controllers/project_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ibutsu_server.db.models import Project
from ibutsu_server.db.models import Result
from ibutsu_server.db.models import User
from ibutsu_server.filters import convert_filter
from ibutsu_server.util.projects import add_user_filter
from ibutsu_server.util.projects import project_has_user
from ibutsu_server.util.query import get_offset
Expand Down Expand Up @@ -77,6 +78,13 @@ def get_project_list(
query = query.filter(Project.owner_id == owner_id)
if group_id:
query = query.filter(Project.group_id == group_id)

if filter_:
for filter_string in filter_:
filter_clause = convert_filter(filter_string, Project)
if filter_clause is not None:
query = query.filter(filter_clause)

offset = get_offset(page, page_size)
total_items = query.count()
total_pages = (total_items // page_size) + (1 if total_items % page_size > 0 else 0)
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
environment:
NODE_ENV: production
REACT_APP_SERVER_URL: http://localhost:8080/api
image: node:14
image: node:18
command: /bin/bash -c 'yarn install && yarn run devserver'
working_dir: /mnt
user: ${UID:-1000}
Expand Down
84 changes: 30 additions & 54 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,50 @@
"version": "2.5.8",
"private": true,
"dependencies": {
"@babel/core": "^7.21.3",
"@babel/eslint-parser": "^7.21.3",
"@babel/core": "^7.24.4",
"@babel/eslint-parser": "^7.24.1",
"@babel/helper-call-delegate": "^7.12.13",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-private-methods": "^7.18.6",
"@babel/plugin-syntax-jsx": "^7.18.6",
"@babel/preset-flow": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"@greatsumini/react-facebook-login": "^2.1.5",
"@monaco-editor/react": "4.4.6",
"@patternfly/patternfly": "^4.224.2",
"@patternfly/react-charts": "^6.94.18",
"@patternfly/react-core": "^4.276.9",
"@patternfly/react-icons": "^4.93.6",
"@patternfly/react-styles": "^4.92.6",
"@patternfly/react-table": "^4.112.39",
"@react-keycloak/web": "^3.4.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.7",
"@babel/plugin-syntax-jsx": "^7.24.1",
"@babel/plugin-transform-class-properties": "^7.24.1",
"@babel/plugin-transform-private-methods": "^7.24.1",
"@babel/preset-flow": "^7.24.1",
"@babel/preset-react": "^7.24.1",
"@greatsumini/react-facebook-login": "^3.3.3",
"@monaco-editor/react": "^4.6.0",
"@patternfly/patternfly": "^5.2.1",
"@patternfly/react-charts": "^7.2.2",
"@patternfly/react-core": "^5.2.3",
"@patternfly/react-icons": "^5.2.1",
"@patternfly/react-styles": "^5.2.1",
"@patternfly/react-table": "^5.2.4",
"@react-oauth/google": "^0.12.1",
"@testing-library/react": "^14.3.0",
"cypress": "^10.11.0",
"enzyme": "^3.11.0",
"eslint": "^7.32.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.34.1",
"keycloak-js": "^19.0.3",
"monaco-editor": "^0.47.0",
"prop-types": "^15.8.1",
"react": "^17.0.0",
"react-dom": "^17.0.2",
"react-google-login": "^5.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-heatmap-grid": "^0.9.0",
"react-json-view": "^1.21.3",
"react-json-tree": "^0.19.0",
"react-linkify": "^1.0.0-alpha",
"react-password-strength-bar": "^0.3.5",
"react-router-dom": "^5.3.4",
"react-scripts": "^4.0.3",
"react-password-strength-bar": "^0.4.1",
"react-router-dom": "^6.22.3",
"react-scripts": "^5.0.1",
"react-simple-oauth2-login": "^0.5.4",
"serve": "^12.0.1",
"typescript": "^4.9.5",
"wolfy87-eventemitter": "^5.2.9"
},
"resolutions": {
"victory-core": "~36.8.6",
"victory-legend": "~36.8.6",
"victory-pie": "~36.8.6",
"victory-tooltip": "~36.8.6",
"victory-chart": "~36.8.6",
"victory-axis": "~36.8.6",
"victory-bar": "~36.8.6",
"victory-stack": "~36.8.6",
"victory-area": "~36.8.6",
"victory-create-container": "~36.8.6"
},
"overrides": {
"victory-core": "~36.8.6",
"victory-legend": "~36.8.6",
"victory-pie": "~36.8.6",
"victory-tooltip": "~36.8.6",
"victory-chart": "~36.8.6",
"victory-axis": "~36.8.6",
"victory-bar": "~36.8.6",
"victory-stack": "~36.8.6",
"victory-area": "~36.8.6",
"victory-create-container": "~36.8.6"
},
"scripts": {
"start": "serve -s build -l tcp://0.0.0.0:8080",
"build": "./bin/write-version-file.js && react-scripts build",
"test": "./bin/write-version-file.js && react-scripts test && cypress run --component",
"test": "./bin/write-version-file.js && react-scripts test --transformIgnorePatterns \"node_modules/(?!@patternfly)/\" && cypress run --component",
"eject": "react-scripts eject",
"devserver": "./bin/write-version-file.js && CI=true react-scripts start",
"lint": "eslint --ext=js --ext=jsx ./src"
Expand All @@ -93,8 +69,8 @@
"requireConfigFile": false,
"babelOptions": {
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-private-methods",
"@babel/plugin-syntax-jsx"
],
"presets": [
Expand Down
29 changes: 15 additions & 14 deletions frontend/src/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
NavList
} from '@patternfly/react-core';

import { NavLink, Route, Switch } from 'react-router-dom';
import { NavLink, Route, Routes } from 'react-router-dom';
import EventEmitter from 'wolfy87-eventemitter';
import ElementWrapper from './components/elementWrapper';

import { IbutsuPage } from './components';
import { AdminHome } from './pages/admin/home';
Expand Down Expand Up @@ -36,14 +37,14 @@ export class Admin extends React.Component {
const navigation = (
<Nav onSelect={this.onNavSelect} theme="dark" aria-label="Nav">
<NavList>
<li className="pf-c-nav__item">
<NavLink to="/admin" className="pf-c-nav__link" activeClassName="pf-m-active" exact>Admin Home</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/admin" className="pf-v5-c-nav__link">Admin Home</NavLink>
</li>
<li className="pf-c-nav__item">
<NavLink to="/admin/users" className="pf-c-nav__link" activeClassName="pf-m-active">Users</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/admin/users" className="pf-v5-c-nav__link">Users</NavLink>
</li>
<li className="pf-c-nav__item">
<NavLink to="/admin/projects" className="pf-c-nav__link" activeClassName="pf-m-active">Projects</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/admin/projects" className="pf-v5-c-nav__link">Projects</NavLink>
</li>
</NavList>
</Nav>
Expand All @@ -52,13 +53,13 @@ export class Admin extends React.Component {
return (
<React.Fragment>
<IbutsuPage eventEmitter={this.eventEmitter} navigation={navigation} title="Administration | Ibutsu">
<Switch>
<Route path="/admin" component={AdminHome} exact />
<Route path="/admin/users" component={UserList} exact />
<Route path="/admin/users/:id" component={UserEdit} />
<Route path="/admin/projects" component={ProjectList} exact />
<Route path="/admin/projects/:id" component={ProjectEdit} exact />
</Switch>
<Routes>
<Route path="*" element={<AdminHome />}/>
<Route path="/users" element={<ElementWrapper routeElement={UserList} />} />
<Route path="/users/:id" element={<ElementWrapper routeElement={UserEdit} />} />
<Route path="/projects" element={<ElementWrapper routeElement={ProjectList} />} />
<Route path="/projects/:id" element={<ElementWrapper routeElement={ProjectEdit} />} />
</Routes>
</IbutsuPage>
</React.Fragment>
);
Expand Down
67 changes: 23 additions & 44 deletions frontend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
NavList
} from '@patternfly/react-core';
import EventEmitter from 'wolfy87-eventemitter';
import ElementWrapper from './components/elementWrapper';

import { NavLink, Route, Switch } from 'react-router-dom';
import { NavLink, Route, Routes } from 'react-router-dom';

import { Dashboard } from './dashboard';
import { ReportBuilder } from './report-builder';
Expand Down Expand Up @@ -68,22 +69,24 @@ export class App extends React.Component {
const navigation = (
<Nav onSelect={this.onNavSelect} theme="dark" aria-label="Nav">
<NavList>
<li className="pf-c-nav__item">
<NavLink to="/" className="pf-c-nav__link" activeClassName="pf-m-active" exact>Dashboard</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/" className="pf-v5-c-nav__link">Dashboard</NavLink>
</li>
<li className="pf-c-nav__item">
<NavLink to="/runs" className="pf-c-nav__link" activeClassName="pf-m-active">Runs</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/runs" className="pf-v5-c-nav__link">Runs</NavLink>
</li>
<li className="pf-c-nav__item">
<NavLink to="/results" className="pf-c-nav__link" activeClassName="pf-m-active">Test Results</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/results" className="pf-v5-c-nav__link">Test Results</NavLink>
</li>
<li className="pf-c-nav__item">
<NavLink to="/reports" className="pf-c-nav__link" activeClassName="pf-m-active">Report Builder</NavLink>
<li className="pf-v5-c-nav__item">
<NavLink to="/reports" className="pf-v5-c-nav__link">Report Builder</NavLink>
</li>
{views && views.map(view => (
<li className="pf-c-nav__item" key={view.id}>
<NavLink to={`/view/${view.id}`} className="pf-c-nav__link" activeClassName="pf-m-active">{view.title}</NavLink>
view.widget !== "jenkins-analysis-view" && (
<li className="pf-v5-c-nav__item" key={view.id}>
<NavLink to={`/view/${view.id}`} className="pf-v5-c-nav__link">{view.title}</NavLink>
</li>
)
))}
</NavList>
</Nav>
Expand All @@ -92,39 +95,15 @@ export class App extends React.Component {
return (
<React.Fragment>
<IbutsuPage eventEmitter={this.eventEmitter} navigation={navigation}>
<Switch>
<Route
path="/"
exact
render={routerProps => (
<Dashboard eventEmitter={this.eventEmitter} {...routerProps} />
)}
/>
<Route
path="/runs"
exact
render={routerProps => (
<RunList eventEmitter={this.eventEmitter} {...routerProps} />
)}
/>
<Route
path="/results"
exact
render={routerProps => (
<ResultList eventEmitter={this.eventEmitter} {...routerProps} />
)}
/>
<Route
path="/reports"
exact
render={routerProps => (
<ReportBuilder eventEmitter={this.eventEmitter} {...routerProps} />
)}
/>
<Route path="/runs/:id" component={Run} />
<Route path="/results/:id" component={Result} />
<Route path="/view/:id" component={View} />
</Switch>
<Routes>
<Route path="*" element={<Dashboard eventEmitter={this.eventEmitter} />} />
<Route path="/runs" element={<ElementWrapper routeElement={RunList} eventEmitter={this.eventEmitter} />} />
<Route path="/results" element={<ElementWrapper routeElement={ResultList} eventEmitter={this.eventEmitter} />} />
<Route path="/reports" element={<ElementWrapper routeElement={ReportBuilder} eventEmitter={this.eventEmitter} />} />
<Route path="/runs/:id" element={<ElementWrapper routeElement={Run} />} />
<Route path="/results/:id" element={<ElementWrapper routeElement={Result} />} />
<Route path="/view/:id" element={<ElementWrapper routeElement={View} />} />
</Routes>
</IbutsuPage>
</React.Fragment>
);
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/app.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { render } from '@testing-library/react';
import { Base } from './base';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Base />, div);
ReactDOM.unmountComponentAtNode(div);
render(<Base />);
});
31 changes: 20 additions & 11 deletions frontend/src/base.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
import React from 'react';

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import { App } from './app';
import { Admin } from './admin';
import { Profile } from './profile';
import { Login } from './login';
import { SignUp } from './sign-up';
import { ForgotPassword } from './forgot-password';
import { ResetPassword } from './reset-password';
import { ProtectedRoute } from './components/protected-route';
import { AuthService } from './services/auth';
import ElementWrapper from './components/elementWrapper';

export const Base = () => {
return (
<Router>
<Switch>
<Route path="/login" exact component={Login} />
<Route path="/sign-up" exact component={SignUp} />
<Route path="/forgot-password" exact component={ForgotPassword} />
<Route path="/reset-password/:activationCode" exact component={ResetPassword} />
<ProtectedRoute path="/profile*" exact isLoggedIn={AuthService.isLoggedIn()} redirectRoute="/login" component={Profile} />
<ProtectedRoute path="/admin*" exact isLoggedIn={AuthService.isLoggedIn()} redirectRoute="/" component={Admin} />
<ProtectedRoute path="/" isLoggedIn={AuthService.isLoggedIn()} redirectRoute="/login" component={App} />
</Switch>
<Routes>
<Route path="/login" element={<ElementWrapper routeElement={Login} />} />
<Route path="/sign-up" element={<ElementWrapper routeElement={SignUp} />} />
<Route path="/forgot-password" element={<ElementWrapper routeElement={ForgotPassword} />} />
<Route path="/reset-password/:activationCode" element={<ElementWrapper routeElement={ResetPassword} />} />
<Route
path="/profile/*"
element={AuthService.isLoggedIn() ? <Profile /> : <Navigate to="/login" />}
/>
<Route
path="/admin/*"
element={AuthService.isLoggedIn() ? <Admin /> : <Navigate to="/" />}
/>
<Route
path="*"
element={AuthService.isLoggedIn() ? <App /> : <Navigate to="/login" />}
/>
</Routes>
</Router>
);
};
Loading

0 comments on commit 0848b65

Please sign in to comment.