diff --git a/js/actions/UsersActions.js b/js/actions/UsersActions.js index 718372f6..dfbfdb8d 100644 --- a/js/actions/UsersActions.js +++ b/js/actions/UsersActions.js @@ -1,14 +1,17 @@ import * as ActionConstants from "../constants/ActionConstants"; import {axiosBackend} from "./index"; import {API_URL} from '../../config'; +import {publishMessage} from "./MessageActions"; +import {errorMessage} from "../model/Message"; export function loadUsers() { return function (dispatch) { dispatch(loadUsersPending()); - axiosBackend.get(`${API_URL}/rest/users`).then((response) => { + return axiosBackend.get(`${API_URL}/rest/users`).then((response) => { dispatch(loadUsersSuccess(response.data)); }).catch((error) => { dispatch(loadUsersError(error.response.data)); + dispatch(publishMessage(errorMessage('users.loading-error', {error: error.response.data.message}))); }); } } diff --git a/js/components/AlertMessage.js b/js/components/AlertMessage.js index ec34c0b8..40523eab 100644 --- a/js/components/AlertMessage.js +++ b/js/components/AlertMessage.js @@ -3,7 +3,7 @@ import {Alert} from "react-bootstrap"; import PropTypes from "prop-types"; const AlertMessage = (props) => ( -
+
{props.message} diff --git a/js/components/login/Login.js b/js/components/login/Login.js index 094c5113..80912844 100644 --- a/js/components/login/Login.js +++ b/js/components/login/Login.js @@ -73,7 +73,7 @@ class Login extends React.Component { {this.i18n('login.title')} {!this.state.deviceSupported && -
+
{this.i18n('Your browser is not fully supported! Some parts of web may not work properly.')}
{this.i18n('We recommend using the latest version of ')} diff --git a/js/components/user/UserRow.js b/js/components/user/UserRow.js index 46b00ee6..9799061f 100644 --- a/js/components/user/UserRow.js +++ b/js/components/user/UserRow.js @@ -1,18 +1,17 @@ import React from "react"; -import {injectIntl} from "react-intl"; -import withI18n from "../../i18n/withI18n"; import {Button} from "react-bootstrap"; -import {LoaderSmall} from "../Loader"; import PropTypes from "prop-types"; import IfInternalAuth from "../misc/oidc/IfInternalAuth"; +import {useI18n} from "../../hooks/useI18n"; let UserRow = (props) => { const user = props.user; + const {i18n} = useI18n(); return {user.username} @@ -20,11 +19,11 @@ let UserRow = (props) => { {user.emailAddress} - - + + ; @@ -34,8 +33,6 @@ UserRow.propTypes = { user: PropTypes.object.isRequired, onEdit: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, - deletionLoading: PropTypes.bool.isRequired, - i18n: PropTypes.func.isRequired, }; -export default injectIntl(withI18n(UserRow)); +export default UserRow; diff --git a/js/components/user/UserTable.js b/js/components/user/UserTable.js index 2080e084..b8bddebe 100644 --- a/js/components/user/UserTable.js +++ b/js/components/user/UserTable.js @@ -1,20 +1,16 @@ -'use strict'; - import React from "react"; import {Table} from "react-bootstrap"; import DeleteItemDialog from "../DeleteItemDialog"; import {injectIntl} from "react-intl"; import withI18n from "../../i18n/withI18n"; import UserRow from "./UserRow"; -import {ACTION_STATUS} from "../../constants/DefaultConstants"; import PropTypes from "prop-types"; import IfInternalAuth from "../misc/oidc/IfInternalAuth"; class UserTable extends React.Component { static propTypes = { users: PropTypes.array.isRequired, - handlers: PropTypes.object.isRequired, - userDeleted: PropTypes.object + handlers: PropTypes.object.isRequired }; constructor(props) { @@ -77,13 +73,11 @@ class UserTable extends React.Component { } _renderUsers() { - const {users, userDeleted} = this.props; + const {users} = this.props; const onEdit = this.props.handlers.onEdit; let rows = []; for (let i = 0, len = users.length; i < len; i++) { - rows.push(); + rows.push(); } return rows; } diff --git a/js/components/user/Users.js b/js/components/user/Users.js index c2f72df8..29cdb6c4 100644 --- a/js/components/user/Users.js +++ b/js/components/user/Users.js @@ -1,64 +1,33 @@ -'use strict'; - import React from 'react'; import {Button, Card} from 'react-bootstrap'; -import {injectIntl} from "react-intl"; -import withI18n from '../../i18n/withI18n'; import UserTable from './UserTable'; -import {ACTION_STATUS, ALERT_TYPES} from "../../constants/DefaultConstants"; -import AlertMessage from "../AlertMessage"; -import {LoaderCard, LoaderSmall} from "../Loader"; import PropTypes from "prop-types"; import IfInternalAuth from "../misc/oidc/IfInternalAuth"; +import PromiseTrackingMask from "../misc/PromiseTrackingMask"; +import {useI18n} from "../../hooks/useI18n"; -class Users extends React.Component { - static propTypes = { - usersLoaded: PropTypes.object, - handlers: PropTypes.object.isRequired, - userDeleted: PropTypes.object, - showAlert: PropTypes.bool.isRequired, - }; - - constructor(props) { - super(props); - this.i18n = this.props.i18n; - } - - render() { - const {usersLoaded, showAlert, userDeleted} = this.props; - if (!usersLoaded.users && (!usersLoaded.status || usersLoaded.status === ACTION_STATUS.PENDING)) { - return ; - } else if (usersLoaded.status === ACTION_STATUS.ERROR) { - return - } - return - - {this.i18n('users.panel-title')} - {this.props.usersLoaded.status === ACTION_STATUS.PENDING && } - - - - -
- -
-
- {showAlert && userDeleted.status === ACTION_STATUS.ERROR && - } - {showAlert && userDeleted.status === ACTION_STATUS.SUCCESS && - } -
-
; - } +const Users = ({usersLoaded, handlers}) => { + const {i18n} = useI18n(); + return + + {i18n('users.panel-title')} + + + + {usersLoaded.users && } + +
+ +
+
+
+
; +}; - _renderHeader() { - return - {this.i18n('users.panel-title')}{this.props.usersLoaded.status === ACTION_STATUS.PENDING && } - ; - } -} +Users.propTypes = { + usersLoaded: PropTypes.object, + handlers: PropTypes.object.isRequired +}; -export default injectIntl(withI18n(Users)); +export default Users; diff --git a/js/components/user/UsersController.js b/js/components/user/UsersController.js index d49fabc5..ab0b1024 100644 --- a/js/components/user/UsersController.js +++ b/js/components/user/UsersController.js @@ -11,6 +11,7 @@ import {bindActionCreators} from "redux"; import {loadUsers} from "../../actions/UsersActions"; import {ROLE} from "../../constants/DefaultConstants"; import {deleteUser} from "../../actions/UserActions"; +import {trackPromise} from "react-promise-tracker"; class UsersController extends React.Component { constructor(props) { @@ -21,7 +22,7 @@ class UsersController extends React.Component { } componentDidMount() { - this.props.loadUsers(); + trackPromise(this.props.loadUsers(), "users"); } _onEditUser = (user) => { diff --git a/tests/__tests__/actions/UsersActions.spec.js b/tests/__tests__/actions/UsersActions.spec.js index 97c13435..813da778 100644 --- a/tests/__tests__/actions/UsersActions.spec.js +++ b/tests/__tests__/actions/UsersActions.spec.js @@ -7,7 +7,7 @@ import {axiosBackend} from "../../../js/actions"; import {loadUsers, loadUsersError, loadUsersPending, loadUsersSuccess} from "../../../js/actions/UsersActions"; import {API_URL} from '../../../config'; -describe('Users synchronize actions', function () { +describe('Users synchronous actions', function () { it('creates an action to fetch all users', () => { const expectedAction = { type: ActionConstants.LOAD_USERS_PENDING, @@ -37,7 +37,7 @@ describe('Users synchronize actions', function () { const middlewares = [thunk.withExtraArgument(axiosBackend)]; const mockStore = configureMockStore(middlewares); -describe('Users asynchronize actions', function () { +describe('Users asynchronous actions', function () { let store, mockApi; const users = [{username: 'test1'}, {username: 'test2'}], @@ -78,7 +78,7 @@ describe('Users asynchronize actions', function () { store.dispatch(loadUsers()); setTimeout(() => { - expect(store.getActions()).toEqual(expectedActions); + expect(store.getActions().slice(0, 2)).toEqual(expectedActions); done(); }, TEST_TIMEOUT); }); diff --git a/tests/__tests__/components/Users.spec.js b/tests/__tests__/components/Users.spec.js index ca9c6602..279e695e 100644 --- a/tests/__tests__/components/Users.spec.js +++ b/tests/__tests__/components/Users.spec.js @@ -1,5 +1,3 @@ -'use strict'; - import React from 'react'; import {IntlProvider} from 'react-intl'; import TestUtils from 'react-dom/test-utils'; @@ -12,8 +10,6 @@ describe('Users', function () { let users, usersLoaded, usersLoadedEmpty, - userDeleted, - showAlert, handlers; users = [{ @@ -23,10 +19,6 @@ describe('Users', function () { }]; beforeEach(() => { - showAlert = false; - userDeleted = { - status: ACTION_STATUS.SUCCESS - }; handlers = { onEdit: jest.fn(), onCreate: jest.fn(), @@ -42,40 +34,10 @@ describe('Users', function () { }; }); - it('shows loader', function () { - usersLoaded = { - status: ACTION_STATUS.PENDING - }; - const tree = TestUtils.renderIntoDocument( - - - ); - const result = TestUtils.findRenderedDOMComponentWithClass(tree, 'loader-spin'); - expect(result).not.toBeNull(); - }); - - it('shows error about institutions were not loaded', function () { - usersLoaded = { - status: ACTION_STATUS.ERROR, - error: { - message: "Error" - } - }; - const tree = TestUtils.renderIntoDocument( - - - ); - const alert = TestUtils.scryRenderedDOMComponentsWithClass(tree, "alert-danger"); - expect(alert).not.toBeNull(); - }); - it('renders card with text, that no users were found', function () { const tree = TestUtils.renderIntoDocument( - + ); const cardHeading = TestUtils.findRenderedDOMComponentWithClass(tree, 'card'); expect(cardHeading).not.toBeNull(); @@ -88,8 +50,7 @@ describe('Users', function () { it('renders card with table and table headers', function () { const tree = TestUtils.renderIntoDocument( - + ); const cardHeading = TestUtils.findRenderedDOMComponentWithClass(tree, 'card'); expect(cardHeading).not.toBeNull(); @@ -104,8 +65,7 @@ describe('Users', function () { it('renders "Create user" button and click on it', function () { const tree = TestUtils.renderIntoDocument( - + ); const buttons = TestUtils.scryRenderedDOMComponentsWithTag(tree, "Button"); expect(buttons.length).toEqual(7); @@ -113,37 +73,4 @@ describe('Users', function () { TestUtils.Simulate.click(buttons[6]); // Create User expect(handlers.onCreate).toHaveBeenCalled(); }); - - it('shows successful alert that user was successfully deleted', function () { - showAlert = true; - userDeleted = { - ...userDeleted, - status: ACTION_STATUS.SUCCESS - }; - const tree = TestUtils.renderIntoDocument( - - - ); - const alert = TestUtils.scryRenderedDOMComponentsWithClass(tree, "alert-success"); - expect(alert).not.toBeNull(); - }); - - it('shows unsuccessful alert that user was not deleted', function () { - showAlert = true; - userDeleted = { - ...userDeleted, - status: ACTION_STATUS.ERROR, - error: { - message: "Error" - } - }; - const tree = TestUtils.renderIntoDocument( - - - ); - const alert = TestUtils.scryRenderedDOMComponentsWithClass(tree, "alert-danger"); - expect(alert).not.toBeNull(); - }); }); \ No newline at end of file