forked from wednesday-solutions/nextjs-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Chris <christin@wednesday.is>
- Loading branch information
1 parent
05501ed
commit bfdedc9
Showing
26 changed files
with
1,116 additions
and
1,767 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import loadable from '@utils/loadable'; | ||
|
||
export default loadable(() => import('./index')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* | ||
* Info Container | ||
* | ||
*/ | ||
import Text from '@app/components/Text'; | ||
import fonts from '@app/themes/fonts'; | ||
import { useInjectSaga } from '@app/utils/injectSaga'; | ||
import { Container } from '@components/styled'; | ||
import Title from '@components/Title'; | ||
import { Card, Col, Row, Skeleton } from 'antd'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import { useRouter } from 'next/router'; | ||
import PropTypes from 'prop-types'; | ||
import React, { useEffect } from 'react'; | ||
import { injectIntl } from 'react-intl'; | ||
import { connect } from 'react-redux'; | ||
import { compose } from 'redux'; | ||
import injectSaga from '@utils/injectSaga'; | ||
import { createStructuredSelector } from 'reselect'; | ||
|
||
import { infoCreators } from './reducer'; | ||
import saga from './saga'; | ||
import { selectInfoData, selectInfoLoading } from './selectors'; | ||
|
||
export function Info({ details, params, loading, dispatchRequestInfo, fallBackDetails }) { | ||
const router = useRouter(); | ||
const { query } = router; | ||
const { name, description, stargazersCount } = { ...(details || {}), ...(fallBackDetails || {}) } || {}; | ||
useInjectSaga({ key: 'info', saga }); | ||
useEffect(() => { | ||
if (isEmpty(details) && !!params?.name && !!query?.owner) { | ||
dispatchRequestInfo(params.name, query.owner); | ||
} | ||
}, [params]); | ||
|
||
const shouldLoad = loading || isEmpty({ ...fallBackDetails, ...details }); | ||
|
||
return ( | ||
<Row justify="center" align="middle" style={{ height: '100vh' }} flex="1 1 90%"> | ||
<Col> | ||
<Container style={{ minWidth: '30rem' }} padding={20}> | ||
<Card | ||
title={React.createElement(Title, { | ||
name, | ||
loading: shouldLoad, | ||
stargazersCount | ||
})} | ||
> | ||
<Skeleton loading={shouldLoad} active> | ||
<Text styles={fonts.style.subheading()}>{description}</Text> | ||
</Skeleton> | ||
</Card> | ||
</Container> | ||
</Col> | ||
</Row> | ||
); | ||
} | ||
|
||
Info.propTypes = { | ||
details: PropTypes.shape({ | ||
name: PropTypes.string.isRequired, | ||
description: PropTypes.string.isRequired, | ||
stargazersCount: PropTypes.number.isRequired | ||
}), | ||
params: PropTypes.shape({ | ||
name: PropTypes.string.isRequired | ||
}), | ||
loading: PropTypes.bool.isRequired, | ||
dispatchRequestInfo: PropTypes.func.isRequired, | ||
fallBackDetails: PropTypes.object | ||
}; | ||
|
||
const mapStateToProps = createStructuredSelector({ | ||
loading: selectInfoLoading(), | ||
fallBackDetails: selectInfoData() | ||
}); | ||
|
||
function mapDispatchToProps(dispatch) { | ||
return { | ||
dispatchRequestInfo: (repo, owner) => dispatch(infoCreators.requestInfo(repo, owner)) | ||
}; | ||
} | ||
|
||
const withConnect = connect(mapStateToProps, mapDispatchToProps); | ||
|
||
export const InfoTest = compose(injectIntl)(Info); | ||
|
||
export default compose(withConnect, injectSaga({ key: 'info', saga }))(Info); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* | ||
* /info reducer | ||
* | ||
*/ | ||
import produce from 'immer'; | ||
import { createActions } from 'reduxsauce'; | ||
import { setError, setData, startLoading, stopLoading, PAYLOAD } from '../../utils/reducer'; | ||
|
||
export const INFO_PAYLOAD = { | ||
REPO: 'repo', | ||
OWNER: 'owner' | ||
}; | ||
|
||
export const initialState = { [PAYLOAD.LOADING]: false, [PAYLOAD.DATA]: {}, [PAYLOAD.ERROR]: null }; | ||
|
||
export const { Types: infoTypes, Creators: infoCreators } = createActions({ | ||
requestInfo: [INFO_PAYLOAD.REPO, INFO_PAYLOAD.OWNER], | ||
successInfo: [PAYLOAD.DATA], | ||
failureInfo: [PAYLOAD.ERROR] | ||
}); | ||
|
||
export const infoReducer = (state = initialState, action) => | ||
produce(state, (draft) => { | ||
switch (action.type) { | ||
case infoTypes.REQUEST_INFO: | ||
startLoading(draft); | ||
break; | ||
case infoTypes.SUCCESS_INFO: | ||
stopLoading(draft); | ||
setData(draft, action); | ||
break; | ||
case infoTypes.FAILURE_INFO: | ||
stopLoading(draft); | ||
setError(draft); | ||
break; | ||
default: | ||
draft = initialState; | ||
} | ||
}); | ||
|
||
export default infoReducer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { put, call, takeLatest } from 'redux-saga/effects'; | ||
import { getRepo } from '@services/info'; | ||
import { ERRORS } from '@app/utils/constants'; | ||
import { infoTypes, infoCreators, INFO_PAYLOAD } from './reducer'; | ||
|
||
export function* requestInfo(action) { | ||
try { | ||
if (action[INFO_PAYLOAD.REPO] || action[INFO_PAYLOAD.OWNER]) { | ||
throw new Error(ERRORS.INSUFFICIENT_INFO); | ||
} | ||
const response = yield call(getRepo, action[INFO_PAYLOAD.REPO], action[INFO_PAYLOAD.OWNER]); | ||
yield put(infoCreators.successInfo(response)); | ||
} catch (error) { | ||
console.error(error.message); | ||
yield put(infoCreators.failureInfo(error.message)); | ||
} | ||
} | ||
|
||
export default function* appSaga() { | ||
yield takeLatest(infoTypes.REQUEST_INFO, requestInfo); | ||
} |
8 changes: 5 additions & 3 deletions
8
app/store/selectors/info.js → app/containers/Info/selectors.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import { PAYLOAD } from '@app/utils/reducer'; | ||
import { createSelector } from 'reselect'; | ||
import { initialState } from '../reducers/info'; | ||
import get from 'lodash/get'; | ||
import { initialState } from './reducer'; | ||
|
||
const selectInfoDomain = (state) => state.info || initialState; | ||
|
||
export const selectInfoLoading = () => createSelector(selectInfoDomain, (substate) => substate.get('loading')); | ||
export const selectInfoData = () => createSelector(selectInfoDomain, (substate) => substate.get('data')); | ||
export const selectInfoLoading = () => createSelector(selectInfoDomain, (substate) => get(substate, PAYLOAD.LOADING)); | ||
export const selectInfoData = () => createSelector(selectInfoDomain, (substate) => get(substate, PAYLOAD.DATA, {})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* | ||
* Tests for Info container | ||
* | ||
* | ||
*/ | ||
|
||
import React from 'react'; | ||
// import { fireEvent } from '@testing-library/dom'; | ||
import { renderProvider } from '@utils/testUtils'; | ||
import { InfoTest as Info } from '../index'; | ||
|
||
describe('<Info /> container tests', () => { | ||
// let submitSpy | ||
|
||
beforeEach(() => { | ||
// submitSpy = jest.fn(); | ||
}); | ||
|
||
it('should render and match the snapshot', () => { | ||
const { baseElement } = renderProvider(<Info />); | ||
expect(baseElement).toMatchSnapshot(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { infoReducer, infoTypes, initialState } from '../reducer'; | ||
|
||
describe('Info reducer tests', () => { | ||
it('should return the initial state by default', () => { | ||
expect(infoReducer(undefined, {})).toEqual(initialState); | ||
}); | ||
|
||
it('should return the updated state when an action of type DEFAULT is dispatched', () => { | ||
const expectedResult = { ...initialState, somePayLoad: 'Mohammed Ali Chherawalla' }; | ||
expect( | ||
infoReducer(initialState, { | ||
type: infoTypes.DEFAULT_ACTION, | ||
somePayLoad: 'Mohammed Ali Chherawalla' | ||
}) | ||
).toEqual(expectedResult); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* Test info sagas | ||
*/ | ||
|
||
import { takeLatest } from 'redux-saga/effects'; | ||
import infoSaga, { defaultFunction } from '../saga'; | ||
import { infoTypes } from '../reducer'; | ||
|
||
describe('Info saga tests', () => { | ||
const generator = infoSaga(); | ||
|
||
it('should start task to watch for DEFAULT_ACTION action', () => { | ||
expect(generator.next().value).toEqual(takeLatest(infoTypes.DEFAULT_ACTION, defaultFunction)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { selectInfo, selectSomePayLoad } from '../selectors'; | ||
|
||
describe('Info selector tests', () => { | ||
const mockedState = { | ||
info: { | ||
somePayLoad: 'W.S' | ||
} | ||
}; | ||
|
||
it('should select the info state', () => { | ||
const infoSelector = selectInfo(); | ||
expect(infoSelector(mockedState)).toEqual(mockedState.info); | ||
}); | ||
|
||
it('should select the somePayLoad state', () => { | ||
const somePayLoadSelector = selectSomePayLoad(); | ||
expect(somePayLoadSelector(mockedState)).toEqual(mockedState.info.somePayLoad); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.