From 78440239384729765f162cf49b0ac772d1311ea3 Mon Sep 17 00:00:00 2001 From: spiros dimopulos Date: Wed, 11 Jun 2025 19:08:58 +0300 Subject: [PATCH 1/3] NOJIRA: refactored AdminPage, AuthorizedPage from using redux connect to hooks --- src/components/pages/AdminPage.jsx | 10 ++-- src/components/pages/AuthorizedPage.jsx | 28 ++++++----- src/components/pages/RootPage.jsx | 8 ++-- src/containers/login/AuthStatusContainer.js | 8 ++-- src/containers/login/LoginModalContainer.js | 8 ++-- src/containers/pages/AdminPageContainer.js | 16 ------- .../pages/AuthorizedPageContainer.js | 26 ----------- src/reducers/index.js | 4 +- .../pages/AdminPageContainer.spec.jsx | 46 ------------------- test/specs/reducers/index.spec.js | 8 ++-- 10 files changed, 39 insertions(+), 123 deletions(-) delete mode 100644 src/containers/pages/AdminPageContainer.js delete mode 100644 src/containers/pages/AuthorizedPageContainer.js delete mode 100644 test/specs/containers/pages/AdminPageContainer.spec.jsx diff --git a/src/components/pages/AdminPage.jsx b/src/components/pages/AdminPage.jsx index ba07836e0..26d618a72 100644 --- a/src/components/pages/AdminPage.jsx +++ b/src/components/pages/AdminPage.jsx @@ -2,13 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { defineMessages, FormattedMessage } from 'react-intl'; import { Redirect, Route, Switch } from 'react-router'; -import Immutable from 'immutable'; +import { useSelector } from 'react-redux'; import AccountPageContainer from '../../containers/pages/AccountPageContainer'; import AuthRolePageContainer from '../../containers/pages/AuthRolePageContainer'; import AdminNavBar from '../admin/AdminNavBar'; import TitleBar from '../sections/TitleBar'; import { canList } from '../../helpers/permissionHelpers'; import styles from '../../../styles/cspace-ui/AdminPage.css'; +import { getAdminTab, getUserPerms } from '../../reducers'; const messages = defineMessages({ title: { @@ -26,17 +27,16 @@ const propTypes = { match: PropTypes.shape({ url: PropTypes.string, }), - perms: PropTypes.instanceOf(Immutable.Map), - preferredTab: PropTypes.string, }; export default function AdminPage(props) { const { match, - perms, - preferredTab, } = props; + const perms = useSelector(getUserPerms); + const preferredTab = useSelector(getAdminTab); + const basename = match.url; const title = ; const permittedTabs = tabs.filter((tab) => canList(tab, perms)); diff --git a/src/components/pages/AuthorizedPage.jsx b/src/components/pages/AuthorizedPage.jsx index 7724510ca..c5db39668 100644 --- a/src/components/pages/AuthorizedPage.jsx +++ b/src/components/pages/AuthorizedPage.jsx @@ -1,21 +1,23 @@ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; -import Immutable from 'immutable'; +import { useDispatch, useSelector } from 'react-redux'; import LoginForm from '../login/LoginForm'; import styles from '../../../styles/cspace-ui/AuthorizedPage.css'; +import { + getLoginError, + getLoginLandingPath, + getIsLoginPending, + getIsLoginSuccess, +} from '../../reducers'; +import { receiveAuthCode } from '../../actions/login'; const propTypes = { history: PropTypes.shape({ replace: PropTypes.func.isRequired, }).isRequired, - isLoginPending: PropTypes.bool, - isLoginSuccess: PropTypes.bool, - landingPath: PropTypes.string, location: PropTypes.shape({ search: PropTypes.string, }).isRequired, - loginError: PropTypes.instanceOf(Immutable.Map), - receiveAuthCode: PropTypes.func.isRequired, }; const contextTypes = { @@ -30,13 +32,16 @@ export default function AuthorizedPage(props, context = {}) { } = context; const { - isLoginPending, - isLoginSuccess, location, - loginError, - receiveAuthCode, } = props; + const isLoginPending = useSelector(getIsLoginPending); + const isLoginSuccess = useSelector(getIsLoginSuccess); + const landingPath = useSelector(getLoginLandingPath); + const loginError = useSelector(getLoginError); + + const dispatch = useDispatch(); + useEffect(() => { const { search, @@ -46,14 +51,13 @@ export default function AuthorizedPage(props, context = {}) { const authCodeRequestId = params.get('state'); const authCode = params.get('code'); - receiveAuthCode(config, authCodeRequestId, authCode); + dispatch(receiveAuthCode(config, authCodeRequestId, authCode)); }, []); useEffect(() => { if (isLoginSuccess) { const { history, - landingPath, } = props; history.replace(landingPath); diff --git a/src/components/pages/RootPage.jsx b/src/components/pages/RootPage.jsx index e212f945b..0b27ea1cc 100644 --- a/src/components/pages/RootPage.jsx +++ b/src/components/pages/RootPage.jsx @@ -6,9 +6,7 @@ import { Helmet } from 'react-helmet'; import classNames from 'classnames'; import ProtectedRouteContainer from '../../containers/routes/ProtectedRouteContainer'; import PublicRoute from '../routes/PublicRoute'; -import AdminPageContainer from '../../containers/pages/AdminPageContainer'; import AuthorizePageContainer from '../../containers/pages/AuthorizePageContainer'; -import AuthorizedPageContainer from '../../containers/pages/AuthorizedPageContainer'; import ConfigPage from './ConfigPage'; import ContentViewerPageContainer from '../../containers/pages/ContentViewerPageContainer'; import CreatePageContainer from '../../containers/pages/CreatePageContainer'; @@ -24,6 +22,8 @@ import SearchResultPageContainer from '../../containers/pages/SearchResultPageCo import NotificationBarContainer from '../../containers/notification/NotificationBarContainer'; import styles from '../../../styles/cspace-ui/RootPage.css'; import favicon from '../../../images/favicon.png'; +import AdminPage from './AdminPage'; +import AuthorizedPage from './AuthorizedPage'; const messages = defineMessages({ title: { @@ -67,12 +67,12 @@ function RootPage(props) { - + - + ({ - isPending: isLoginPending(state), - isSuccess: isLoginSuccess(state), + isPending: getIsLoginPending(state), + isSuccess: getIsLoginSuccess(state), username: getLoginUsername(state), error: getLoginError(state), }); diff --git a/src/containers/login/LoginModalContainer.js b/src/containers/login/LoginModalContainer.js index e1d2a8bf6..ee859f3a3 100644 --- a/src/containers/login/LoginModalContainer.js +++ b/src/containers/login/LoginModalContainer.js @@ -4,15 +4,15 @@ import { openLoginWindow } from '../../actions/login'; import { getLoginError, - isLoginPending, - isLoginSuccess, + getIsLoginPending, + getIsLoginSuccess, isLoginWindowOpen, isLoginWindowOpenFailed, } from '../../reducers'; const mapStateToProps = (state) => ({ - isLoginPending: isLoginPending(state), - isLoginSuccess: isLoginSuccess(state), + isLoginPending: getIsLoginPending(state), + isLoginSuccess: getIsLoginSuccess(state), isLoginWindowOpen: isLoginWindowOpen(state), isLoginWindowOpenFailed: isLoginWindowOpenFailed(state), loginError: getLoginError(state), diff --git a/src/containers/pages/AdminPageContainer.js b/src/containers/pages/AdminPageContainer.js deleted file mode 100644 index 8216880b0..000000000 --- a/src/containers/pages/AdminPageContainer.js +++ /dev/null @@ -1,16 +0,0 @@ -import { connect } from 'react-redux'; -import AdminPage from '../../components/pages/AdminPage'; - -import { - getAdminTab, - getUserPerms, -} from '../../reducers'; - -const mapStateToProps = (state) => ({ - perms: getUserPerms(state), - preferredTab: getAdminTab(state), -}); - -export default connect( - mapStateToProps, -)(AdminPage); diff --git a/src/containers/pages/AuthorizedPageContainer.js b/src/containers/pages/AuthorizedPageContainer.js deleted file mode 100644 index 449a5c798..000000000 --- a/src/containers/pages/AuthorizedPageContainer.js +++ /dev/null @@ -1,26 +0,0 @@ -import { connect } from 'react-redux'; -import AuthorizedPage from '../../components/pages/AuthorizedPage'; -import { receiveAuthCode } from '../../actions/login'; - -import { - getLoginError, - getLoginLandingPath, - isLoginPending, - isLoginSuccess, -} from '../../reducers'; - -const mapStateToProps = (state) => ({ - isLoginPending: isLoginPending(state), - isLoginSuccess: isLoginSuccess(state), - landingPath: getLoginLandingPath(state), - loginError: getLoginError(state), -}); - -const mapDispatchToProps = { - receiveAuthCode, -}; - -export default connect( - mapStateToProps, - mapDispatchToProps, -)(AuthorizedPage); diff --git a/src/reducers/index.js b/src/reducers/index.js index 2d530bdb4..4b7f603a9 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -81,9 +81,9 @@ export const getLoginLandingPath = (state) => fromLogin.getLandingPath(state.log export const getLoginUsername = (state) => fromLogin.getUsername(state.login); -export const isLoginPending = (state) => fromLogin.isPending(state.login); +export const getIsLoginPending = (state) => fromLogin.isPending(state.login); -export const isLoginSuccess = (state) => fromLogin.isSuccess(state.login); +export const getIsLoginSuccess = (state) => fromLogin.isSuccess(state.login); export const getLoginError = (state) => fromLogin.getError(state.login); diff --git a/test/specs/containers/pages/AdminPageContainer.spec.jsx b/test/specs/containers/pages/AdminPageContainer.spec.jsx deleted file mode 100644 index 5194132ba..000000000 --- a/test/specs/containers/pages/AdminPageContainer.spec.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import configureMockStore from 'redux-mock-store'; -import { createRenderer } from 'react-test-renderer/shallow'; -import Immutable from 'immutable'; -import { findWithType } from 'react-shallow-testutils'; -import AdminPage from '../../../../src/components/pages/AdminPage'; -import AdminPageContainer from '../../../../src/containers/pages/AdminPageContainer'; - -chai.should(); - -const mockStore = configureMockStore(); - -const adminTab = 'tabName'; - -const perms = Immutable.fromJS({ - collectionobject: { - data: 'CRUDL', - }, - group: { - data: 'CRUDL', - }, -}); - -const store = mockStore({ - prefs: Immutable.Map({ - adminTab, - }), - user: Immutable.Map({ - perms, - }), -}); - -describe('AdminPageContainer', () => { - it('should set props on AdminPage', () => { - const shallowRenderer = createRenderer(); - - shallowRenderer.render(); - - const result = shallowRenderer.getRenderOutput(); - const admin = findWithType(result, AdminPage); - - admin.type.should.equal(AdminPage); - admin.props.should.have.property('perms', perms); - admin.props.should.have.property('preferredTab', adminTab); - }); -}); diff --git a/test/specs/reducers/index.spec.js b/test/specs/reducers/index.spec.js index 4075b1603..fd6070784 100644 --- a/test/specs/reducers/index.spec.js +++ b/test/specs/reducers/index.spec.js @@ -12,8 +12,8 @@ import reducer, { getUserUsername, getUserPerms, getLoginUsername, - isLoginPending, - isLoginSuccess, + getIsLoginPending, + getIsLoginSuccess, getLoginError, isLogoutPending, getLogoutResponse, @@ -234,7 +234,7 @@ describe('reducer', () => { describe('isLoginPending selector', () => { it('should select from the login key', () => { - isLoginPending({ + getIsLoginPending({ login: Immutable.Map({ isPending: true, }), @@ -256,7 +256,7 @@ describe('reducer', () => { describe('isLoginSuccess selector', () => { it('should select from the login key', () => { - isLoginSuccess({ + getIsLoginSuccess({ login: Immutable.Map({ isSuccess: true, }), From eafbe43c6dde12360ff02d4e4da7fc5c6814b3ec Mon Sep 17 00:00:00 2001 From: spiros dimopulos Date: Thu, 12 Jun 2025 16:34:00 +0300 Subject: [PATCH 2/3] NOJIRA: refactored AuthorizePage from using redux connect to hooks --- src/components/pages/AuthorizePage.jsx | 7 ++++--- src/components/pages/RootPage.jsx | 4 ++-- src/containers/pages/AuthorizePageContainer.js | 15 --------------- 3 files changed, 6 insertions(+), 20 deletions(-) delete mode 100644 src/containers/pages/AuthorizePageContainer.js diff --git a/src/components/pages/AuthorizePage.jsx b/src/components/pages/AuthorizePage.jsx index 6a1f935f2..622fb3591 100644 --- a/src/components/pages/AuthorizePage.jsx +++ b/src/components/pages/AuthorizePage.jsx @@ -3,12 +3,13 @@ import { useEffect } from 'react'; import PropTypes from 'prop-types'; import get from 'lodash/get'; +import { useDispatch } from 'react-redux'; +import { createAuthCodeUrl } from '../../actions/login'; const propTypes = { config: PropTypes.shape({ serverUrl: PropTypes.string, }), - createAuthCodeUrl: PropTypes.func.isRequired, location: PropTypes.shape({ state: PropTypes.object, }).isRequired, @@ -21,8 +22,8 @@ const contextTypes = { }; export default function AuthorizePage(props, context) { + const dispatch = useDispatch(); const { - createAuthCodeUrl, location, } = props; @@ -33,7 +34,7 @@ export default function AuthorizePage(props, context) { const landingPath = get(location, ['state', 'continuation']) || ''; useEffect(() => { - createAuthCodeUrl(config, landingPath).then((url) => { + dispatch(createAuthCodeUrl(config, landingPath)).then((url) => { window.location.replace(url); }); }, []); diff --git a/src/components/pages/RootPage.jsx b/src/components/pages/RootPage.jsx index 0b27ea1cc..3ece40106 100644 --- a/src/components/pages/RootPage.jsx +++ b/src/components/pages/RootPage.jsx @@ -6,7 +6,6 @@ import { Helmet } from 'react-helmet'; import classNames from 'classnames'; import ProtectedRouteContainer from '../../containers/routes/ProtectedRouteContainer'; import PublicRoute from '../routes/PublicRoute'; -import AuthorizePageContainer from '../../containers/pages/AuthorizePageContainer'; import ConfigPage from './ConfigPage'; import ContentViewerPageContainer from '../../containers/pages/ContentViewerPageContainer'; import CreatePageContainer from '../../containers/pages/CreatePageContainer'; @@ -24,6 +23,7 @@ import styles from '../../../styles/cspace-ui/RootPage.css'; import favicon from '../../../images/favicon.png'; import AdminPage from './AdminPage'; import AuthorizedPage from './AuthorizedPage'; +import AuthorizePage from './AuthorizePage'; const messages = defineMessages({ title: { @@ -66,7 +66,7 @@ function RootPage(props) { - + diff --git a/src/containers/pages/AuthorizePageContainer.js b/src/containers/pages/AuthorizePageContainer.js deleted file mode 100644 index e50762d83..000000000 --- a/src/containers/pages/AuthorizePageContainer.js +++ /dev/null @@ -1,15 +0,0 @@ -import { connect } from 'react-redux'; -import AuthorizePage from '../../components/pages/AuthorizePage'; - -import { - createAuthCodeUrl, -} from '../../actions/login'; - -const mapDispatchToProps = { - createAuthCodeUrl, -}; - -export default connect( - undefined, - mapDispatchToProps, -)(AuthorizePage); From 738dbf8d6f09efa7332fe25bc12008cde1751358 Mon Sep 17 00:00:00 2001 From: spiros dimopulos Date: Tue, 8 Jul 2025 17:15:36 +0300 Subject: [PATCH 3/3] NOJIRA: renamed getIsLoginPending, getIsLoginSuccess back to isLoginPending, isLoginSuccess --- src/components/pages/AuthorizedPage.jsx | 8 ++++---- src/containers/login/AuthStatusContainer.js | 8 ++++---- src/containers/login/LoginModalContainer.js | 8 ++++---- src/reducers/index.js | 4 ++-- test/specs/reducers/index.spec.js | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/pages/AuthorizedPage.jsx b/src/components/pages/AuthorizedPage.jsx index c5db39668..928156e63 100644 --- a/src/components/pages/AuthorizedPage.jsx +++ b/src/components/pages/AuthorizedPage.jsx @@ -6,8 +6,8 @@ import styles from '../../../styles/cspace-ui/AuthorizedPage.css'; import { getLoginError, getLoginLandingPath, - getIsLoginPending, - getIsLoginSuccess, + isLoginPending as isLoginPendingSelector, + isLoginSuccess as isLoginSuccessSelector, } from '../../reducers'; import { receiveAuthCode } from '../../actions/login'; @@ -35,8 +35,8 @@ export default function AuthorizedPage(props, context = {}) { location, } = props; - const isLoginPending = useSelector(getIsLoginPending); - const isLoginSuccess = useSelector(getIsLoginSuccess); + const isLoginPending = useSelector(isLoginPendingSelector); + const isLoginSuccess = useSelector(isLoginSuccessSelector); const landingPath = useSelector(getLoginLandingPath); const loginError = useSelector(getLoginError); diff --git a/src/containers/login/AuthStatusContainer.js b/src/containers/login/AuthStatusContainer.js index 39ba57fa3..eef964931 100644 --- a/src/containers/login/AuthStatusContainer.js +++ b/src/containers/login/AuthStatusContainer.js @@ -3,14 +3,14 @@ import AuthStatus from '../../components/login/AuthStatus'; import { getLoginUsername, - getIsLoginPending, - getIsLoginSuccess, + isLoginPending, + isLoginSuccess, getLoginError, } from '../../reducers'; const mapStateToProps = (state) => ({ - isPending: getIsLoginPending(state), - isSuccess: getIsLoginSuccess(state), + isPending: isLoginPending(state), + isSuccess: isLoginSuccess(state), username: getLoginUsername(state), error: getLoginError(state), }); diff --git a/src/containers/login/LoginModalContainer.js b/src/containers/login/LoginModalContainer.js index ee859f3a3..e1d2a8bf6 100644 --- a/src/containers/login/LoginModalContainer.js +++ b/src/containers/login/LoginModalContainer.js @@ -4,15 +4,15 @@ import { openLoginWindow } from '../../actions/login'; import { getLoginError, - getIsLoginPending, - getIsLoginSuccess, + isLoginPending, + isLoginSuccess, isLoginWindowOpen, isLoginWindowOpenFailed, } from '../../reducers'; const mapStateToProps = (state) => ({ - isLoginPending: getIsLoginPending(state), - isLoginSuccess: getIsLoginSuccess(state), + isLoginPending: isLoginPending(state), + isLoginSuccess: isLoginSuccess(state), isLoginWindowOpen: isLoginWindowOpen(state), isLoginWindowOpenFailed: isLoginWindowOpenFailed(state), loginError: getLoginError(state), diff --git a/src/reducers/index.js b/src/reducers/index.js index 4b7f603a9..2d530bdb4 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -81,9 +81,9 @@ export const getLoginLandingPath = (state) => fromLogin.getLandingPath(state.log export const getLoginUsername = (state) => fromLogin.getUsername(state.login); -export const getIsLoginPending = (state) => fromLogin.isPending(state.login); +export const isLoginPending = (state) => fromLogin.isPending(state.login); -export const getIsLoginSuccess = (state) => fromLogin.isSuccess(state.login); +export const isLoginSuccess = (state) => fromLogin.isSuccess(state.login); export const getLoginError = (state) => fromLogin.getError(state.login); diff --git a/test/specs/reducers/index.spec.js b/test/specs/reducers/index.spec.js index fd6070784..4075b1603 100644 --- a/test/specs/reducers/index.spec.js +++ b/test/specs/reducers/index.spec.js @@ -12,8 +12,8 @@ import reducer, { getUserUsername, getUserPerms, getLoginUsername, - getIsLoginPending, - getIsLoginSuccess, + isLoginPending, + isLoginSuccess, getLoginError, isLogoutPending, getLogoutResponse, @@ -234,7 +234,7 @@ describe('reducer', () => { describe('isLoginPending selector', () => { it('should select from the login key', () => { - getIsLoginPending({ + isLoginPending({ login: Immutable.Map({ isPending: true, }), @@ -256,7 +256,7 @@ describe('reducer', () => { describe('isLoginSuccess selector', () => { it('should select from the login key', () => { - getIsLoginSuccess({ + isLoginSuccess({ login: Immutable.Map({ isSuccess: true, }),