Skip to content

Commit

Permalink
fix: render spinner correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
ismay committed Jan 13, 2021
1 parent 7e80e53 commit 65a6b67
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 49 deletions.
19 changes: 15 additions & 4 deletions src/components/AbsoluteCenter/AbsoluteCenter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { node } from 'prop-types'
import { node, bool } from 'prop-types'
import styles from './AbsoluteCenter.module.css'

/**
Expand All @@ -8,12 +8,23 @@ import styles from './AbsoluteCenter.module.css'
* rendered in a row by default.
*/

const AbsoluteCenter = ({ children }) => (
<div className={styles.center}>{children}</div>
)
const AbsoluteCenter = ({ children, vertical }) => {
const classNames = [styles.center]

if (vertical) {
classNames.push(styles.vertical)
}

return <div className={classNames.join(' ')}>{children}</div>
}

AbsoluteCenter.defaultProps = {
vertical: false,
}

AbsoluteCenter.propTypes = {
children: node.isRequired,
vertical: bool,
}

export default AbsoluteCenter
4 changes: 4 additions & 0 deletions src/components/AbsoluteCenter/AbsoluteCenter.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@
right: 0;
pointer-events: none;
}

.vertical {
flex-direction: column;
}
6 changes: 6 additions & 0 deletions src/components/AbsoluteCenter/AbsoluteCenter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ describe('<AbsoluteCenter>', () => {

expect(wrapper).toMatchSnapshot()
})

it('responds to a vertical prop', () => {
const wrapper = shallow(<AbsoluteCenter vertical>Text</AbsoluteCenter>)

expect(wrapper).toMatchSnapshot()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ exports[`<AbsoluteCenter> renders correctly 1`] = `
Text
</div>
`;
exports[`<AbsoluteCenter> responds to a vertical prop 1`] = `
<div
className="center vertical"
>
Text
</div>
`;
2 changes: 1 addition & 1 deletion src/components/Aligner/Aligner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('<Aligner>', () => {
})

it('responds to a vertical prop', () => {
const wrapper = shallow(<Aligner>Text</Aligner>)
const wrapper = shallow(<Aligner vertical>Text</Aligner>)

expect(wrapper).toMatchSnapshot()
})
Expand Down
2 changes: 1 addition & 1 deletion src/components/Aligner/__snapshots__/Aligner.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exports[`<Aligner> renders correctly 1`] = `
exports[`<Aligner> responds to a vertical prop 1`] = `
<div
className="horizontal"
className="vertical"
>
Text
</div>
Expand Down
11 changes: 6 additions & 5 deletions src/components/AuthWall/AuthWall.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AbsoluteCenter } from '../AbsoluteCenter'

export const UnconnectedAuthWall = ({
children,
isFetching,
didFetch,
errorMessage,
isAuthorized,
fetchMeIfNeeded,
Expand All @@ -17,10 +17,11 @@ export const UnconnectedAuthWall = ({
fetchMeIfNeeded()
}, [fetchMeIfNeeded])

if (isFetching) {
if (!didFetch) {
return (
<AbsoluteCenter>
<AbsoluteCenter vertical>
<CircularLoader />
Checking permissions
</AbsoluteCenter>
)
}
Expand All @@ -38,7 +39,7 @@ export const UnconnectedAuthWall = ({

UnconnectedAuthWall.propTypes = {
children: node.isRequired,
isFetching: bool.isRequired,
didFetch: bool.isRequired,
errorMessage: string.isRequired,
isAuthorized: bool.isRequired,
fetchMeIfNeeded: func.isRequired,
Expand All @@ -50,7 +51,7 @@ const mapStateToProps = state => {

/* istanbul ignore next */
return {
isFetching: selectors.getIsFetching(me),
didFetch: selectors.getDidFetch(me),
errorMessage: selectors.getErrorMessage(me),
isAuthorized: selectors.getIsAuthorized(me),
}
Expand Down
14 changes: 9 additions & 5 deletions src/components/AuthWall/AuthWall.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,40 @@ import { UnconnectedAuthWall as AuthWall } from './AuthWall'

const defaultProps = {
children: 'Children',
isFetching: false,
didFetch: false,
errorMessage: '',
isAuthorized: false,
fetchMeIfNeeded: () => {},
}

describe('<AuthWall>', () => {
it('renders a spinner when loading', () => {
const props = { ...defaultProps, isFetching: true }
const props = { ...defaultProps, didFetch: false }
const wrapper = shallow(<AuthWall {...props} />)

expect(wrapper).toMatchSnapshot()
})

it('renders errors if they occur', () => {
const props = { ...defaultProps, errorMessage: 'Something went wrong' }
const props = {
...defaultProps,
didFetch: true,
errorMessage: 'Something went wrong',
}
const wrapper = shallow(<AuthWall {...props} />)

expect(wrapper).toMatchSnapshot()
})

it('renders a message for unauthorized users', () => {
const props = { ...defaultProps, isAuthorized: false }
const props = { ...defaultProps, didFetch: true, isAuthorized: false }
const wrapper = shallow(<AuthWall {...props} />)

expect(wrapper).toMatchSnapshot()
})

it('renders the children for authorized users', () => {
const props = { ...defaultProps, isAuthorized: true }
const props = { ...defaultProps, didFetch: true, isAuthorized: true }
const wrapper = shallow(<AuthWall {...props} />)

expect(wrapper).toMatchSnapshot()
Expand Down
5 changes: 4 additions & 1 deletion src/components/AuthWall/__snapshots__/AuthWall.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ exports[`<AuthWall> renders a message for unauthorized users 1`] = `
`;
exports[`<AuthWall> renders a spinner when loading 1`] = `
<AbsoluteCenter>
<AbsoluteCenter
vertical={true}
>
<CircularLoader />
Checking permissions
</AbsoluteCenter>
`;
Expand Down
6 changes: 6 additions & 0 deletions src/data/me/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ import * as selectors from './reducer'

export const fetchMeSuccess = payload => ({
type: types.FETCH_ME_SUCCESS,
meta: {
receivedAt: Date.now(),
},
payload,
})

export const fetchMeFail = error => ({
type: types.FETCH_ME_FAIL,
meta: {
receivedAt: Date.now(),
},
error,
})

Expand Down
18 changes: 18 additions & 0 deletions src/data/me/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const { origin, pathname } = new URL(endpoints.me)
* Mocks
*/

// Return consistent date for testing
Date.now = jest.fn(() => 1)

// Allow selectors to be mocked
selectors.getShouldFetch = jest.fn() // eslint-disable-line import/namespace
rootSelectors.getMe = jest.fn() // eslint-disable-line import/namespace
Expand All @@ -34,6 +37,9 @@ describe('fetchMeSuccess', () => {
const actual = actions.fetchMeSuccess('payload')
const expected = {
type: types.FETCH_ME_SUCCESS,
meta: {
receivedAt: 1,
},
payload: 'payload',
}

Expand All @@ -46,6 +52,9 @@ describe('fetchMeFail', () => {
const actual = actions.fetchMeFail('error')
const expected = {
type: types.FETCH_ME_FAIL,
meta: {
receivedAt: 1,
},
error: 'error',
}

Expand Down Expand Up @@ -74,6 +83,9 @@ describe('fetchMe', () => {
{ type: types.FETCH_ME },
{
type: types.FETCH_ME_SUCCESS,
meta: {
receivedAt: 1,
},
payload: mockResponse,
},
]
Expand All @@ -94,6 +106,9 @@ describe('fetchMe', () => {
{ type: types.FETCH_ME },
{
type: types.FETCH_ME_FAIL,
meta: {
receivedAt: 1,
},
error: error,
},
]
Expand Down Expand Up @@ -126,6 +141,9 @@ describe('fetchMeIfNeeded', () => {
{ type: types.FETCH_ME },
{
type: types.FETCH_ME_SUCCESS,
meta: {
receivedAt: 1,
},
payload: mockResponse,
},
]
Expand Down
14 changes: 8 additions & 6 deletions src/data/me/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as types from './actionTypes'
const fallbackMessage =
'Something went wrong, but no errormessage was provided.'
const initialState = {
didFetchSuccessfully: false,
lastUpdated: 0,
errorMessage: '',
isFetching: false,
data: {},
Expand All @@ -15,14 +15,14 @@ const reducer = (state = initialState, action) => {
return { ...state, isFetching: true }
case types.FETCH_ME_SUCCESS:
return {
didFetchSuccessfully: true,
lastUpdated: action.meta.receivedAt,
errorMessage: '',
isFetching: false,
data: action.payload,
}
case types.FETCH_ME_FAIL:
return {
...state,
lastUpdated: action.meta.receivedAt,
errorMessage: action.error.message || fallbackMessage,
isFetching: false,
data: {},
Expand All @@ -40,6 +40,7 @@ export default reducer

export const getIsFetching = state => state.isFetching
export const getErrorMessage = state => state.errorMessage
export const getDidFetch = state => !!state.lastUpdated

export const getIsAuthorized = state => {
const { authorities } = state.data
Expand All @@ -57,18 +58,19 @@ export const getIsAuthorized = state => {

export const getShouldFetch = state => {
const {
lastUpdated,
isFetching,
errorMessage,
data: { authorities },
didFetchSuccessfully,
} = state
const hasError = !!errorMessage
const hasFetched = !!lastUpdated
const hasAuthorities = !!authorities

if (isFetching || hasAuthorities) {
return false
}

// Fetch if there's an error, if it hasn't fetched yet or there are no authorities
return hasError || !didFetchSuccessfully || !hasAuthorities
// Fetch if there's an error or if it hasn't fetched yet
return hasError || !hasFetched
}
Loading

0 comments on commit 65a6b67

Please sign in to comment.