Skip to content

Commit

Permalink
#164324997 Log Society Activity points (#188)
Browse files Browse the repository at this point in the history
* Redux Saga setup

* Clean Saga examples

* Onboading screen

* log points

* merge conflicts

* Log society activity points

* Log points reducer tests

* log activity points action tests

* log activity points generators and component tests

* refactor and implement review feedback

* remove evaluation

* update branch

* add expects blocks to my tests

* team feedback implementation

* improve test coverage

* improve test coverage

* improve tests

* Remove unnecessary code

* logged activity should be added to the user activity list

* Rename file

* test componentDidUpdate

* add snapshot

* fix datefns time format error
  • Loading branch information
alexmochu authored and Kachulio1 committed Mar 26, 2019
1 parent 88e6c7f commit be9a821
Show file tree
Hide file tree
Showing 37 changed files with 1,196 additions and 100 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"webpack-merge": "^4.1.5"
},
"dependencies": {
"@material-ui/core": "^3.9.0",
"@material-ui/icons": "^3.0.2",
"bootstrap": "^4.2.1",
"date-fns": "^1.30.1",
"js-cookie": "^2.2.0",
Expand Down
35 changes: 32 additions & 3 deletions src/app/Dashboard/components/DashboardContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { ButtonComponent } from '../../common/components';
import MyStatsComponent from './MyStatsComponent';
import SocietyStatsComponent from './SocietyStatsComponent';
import LogPointsComponent from './LogPointsModalContainer';
import MyActivitiesComponent from './MyActivitiesComponent';

import { myStats } from '../constants';
Expand All @@ -14,6 +15,7 @@ import { getUserInfo, getToken } from '../../utils/tokenIsValid';
export class DashboardContainer extends Component {
state = {
user: {},
logPoints: false,
};

/**
Expand All @@ -30,6 +32,7 @@ export class DashboardContainer extends Component {
activitiesLogged: myStats.activities,
userActivities: myStats.userActivities,
fetchUserActivites: () => {},
loadCategories: () => {},
};

/**
Expand All @@ -45,23 +48,43 @@ export class DashboardContainer extends Component {
pointsEarned: PropTypes.number,
activitiesLogged: PropTypes.number,
fetchUserActivites: PropTypes.func,
loadCategories: PropTypes.func,
userActivities: PropTypes.arrayOf(PropTypes.shape({})),
};

componentDidMount() {
const { fetchUserActivites } = this.props;
const { fetchUserActivites, loadCategories } = this.props;
const token = getToken();
const userInfo = getUserInfo(token);
this.setState({ user: userInfo });
fetchUserActivites(userInfo.id);
loadCategories();
}

logPointsModal = () => {
const { logPoints } = this.state;
this.setState({
logPoints: !logPoints,
});
}

render() {
const {
error, loading, pointsEarned, activitiesLogged, userActivities, society,
} = this.props;
const { user } = this.state;
const {
user, logPoints,
} = this.state;
let dashboardHtml;
let logPointsComponent;
if (logPoints) {
logPointsComponent = (
<LogPointsComponent
open={logPoints}
close={this.logPointsModal}
/>
);
}
if (loading) {
dashboardHtml = <p>Loading ...</p>;
} else if (!loading && error) {
Expand All @@ -81,7 +104,11 @@ export class DashboardContainer extends Component {
<div className='user-dashboard__actions col-sm-12'>
<h3 className='user-dashboard__title'>My Activities</h3>
<div>
<ButtonComponent className='button__add user-dashboard__button'>
<ButtonComponent
type='button'
className='button__add user-dashboard__button'
onClick={this.logPointsModal}
>
<span className='fa fa-plus' />
<span>Log Points</span>
</ButtonComponent>
Expand All @@ -91,6 +118,7 @@ export class DashboardContainer extends Component {
</ButtonComponent>
</div>
</div>
{ logPointsComponent }
<MyActivitiesComponent userActivities={userActivities} />
</div>
);
Expand All @@ -112,5 +140,6 @@ export default connect(
mapStateToProps,
{
fetchUserActivites: actions.fetchUserActivitiesRequest,
loadCategories: actions.loadCategories,
},
)(DashboardContainer);
190 changes: 190 additions & 0 deletions src/app/Dashboard/components/LogPointsModalContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import TextField from '@material-ui/core/TextField';
import StartIcon from '@material-ui/icons/Star';
import { MenuItem, ButtonComponent } from '../../common/components';
import actions from '../operations/actions';

/**
* @summary Renders the Log society points Modal
* @class LogPointsModal
* @extends React.Component
*/
export class LogPointsModal extends Component {
state = {
categoryOption: '',
numberOfParticipants: '',
description: '',
activityDate: '',
selectCategory: {
value: 0,
supportsMultipleParticipants: false,
},
};

static defaultProps = {
categories: [],
open: false,
logActivity: () => {},
close: () => {},
}

static propTypes = {
open: PropTypes.bool,
logActivity: PropTypes.func,
categories: PropTypes.arrayOf(PropTypes.shape({})),
close: PropTypes.func,
};

componentDidUpdate(prevProps, prevState) {
const { categoryOption } = this.state;
const { categories } = this.props;
const categoryItem = categories.find(category => category.id === categoryOption);
this.setFormState(prevState, categoryItem, categoryOption);
}

setFormState = (prevState, categoryItem, categoryOption) => {
if (categoryOption !== prevState.categoryOption) {
this.setState({
selectCategory: categoryItem,
});
}
}

/**
* @name handleChange
* @summary Maps the Society Activity inputs to the local state
* @return {void}
*/
handleChange = prop => (event) => {
this.setState({ [prop]: event.target.value });
};

/**
* @name handleSubmit
* @summary Handles submission of society activity points
* @return {void}
*/
handleSubmit = () => {
const { logActivity, close } = this.props;
const {
categoryOption, activityDate, numberOfParticipants, description,
} = this.state;
logActivity(
{
activityId: categoryOption,
date: activityDate,
noOfParticipants: numberOfParticipants,
description,
},
);
close();
}

render() {
const {
categoryOption,
numberOfParticipants,
description,
activityDate,
selectCategory,
} = this.state;
const {
open, categories, close,
} = this.props;
const styles = {
login: {
transform: open ? 'translateY(0%)' : 'translateY(-100vh)',
opacity: open ? '1' : '0',
},
textField: {
display: selectCategory.supportsMultipleParticipants ? '' : 'none',
},
};
return (
<div className='login-jumbotron'>
<div
className='login-container'
style={styles.login}
>
<div className='log-points-ratings'>
<StartIcon className='ratings-icon' />
<StartIcon className='ratings-icon' />
<StartIcon className='ratings-icon' />
</div>
<div className='log-points'>
<h5 className='log-points__heading'>Log in points</h5>
</div>
<form className='form-container'>
<MenuItem
handleChange={this.handleChange('categoryOption')}
categories={categories}
categoryId={categoryOption}
/>
<TextField
id='filled-number'
name='numberOfParticipants'
label='Number'
value={numberOfParticipants}
onChange={this.handleChange('numberOfParticipants')}
type='number'
style={styles.textField}
margin='normal'
fullWidth
/>
<TextField
required
id='date'
label='Date'
type='date'
value={activityDate}
margin='normal'
InputLabelProps={{
shrink: true,
}}
fullWidth
onChange={this.handleChange('activityDate')}
/>
<TextField
required
id='standard-description'
label='Briefly what did you do'
margin='normal'
fullWidth
value={description}
onChange={this.handleChange('description')}
/>
<div>
<ButtonComponent type='button' className='btn-points'>
{selectCategory.value}
{' '}
Points
</ButtonComponent>
</div>
<div className='log-points-footer'>
<ButtonComponent
type='button'
className='btn-log'
onClick={this.handleSubmit}
>
Log
</ButtonComponent>
<ButtonComponent type='button' className='btn-abort' onClick={close}>Cancel</ButtonComponent>
</div>
</form>
</div>
</div>
);
}
}

export const mapStateToProps = state => ({
categories: state.dashboard.categories,
});


export default connect(mapStateToProps, {
logActivity: activity => actions.logPointsRequest(activity),
})(LogPointsModal);
4 changes: 2 additions & 2 deletions src/app/Dashboard/components/MyActivitiesComponent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import dateFns from 'date-fns';
import { format } from 'date-fns';

import { TableComponent, StatusIndicatorComponent, TruncateDescriptionContainer } from '../../common/components';

Expand All @@ -22,7 +22,7 @@ const MyActivitiesComponent = (props) => {
return (
<tr key={id} className='myactivities__table__row'>
<td>{activity.activity}</td>
<td>{dateFns.format(activityDate, 'MMM DD YYYY')}</td>
<td>{format(new Date(activityDate), 'MMM dd yyyy')}</td>
<td>
<TruncateDescriptionContainer description={description} wordCount={80} />
</td>
Expand Down
15 changes: 12 additions & 3 deletions src/app/Dashboard/components/tests/DashboardContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('<DashboardContainer />', () => {
pointsEarned = 0,
userActivities = [],
activitiesLogged = 0,
society= ''
society = '',
} = {}) => {
const props = {
error,
Expand Down Expand Up @@ -42,7 +42,16 @@ describe('<DashboardContainer />', () => {
});

it('should contain <MyStatsComponent points={300} activities={2} />', () => {
const { shallowWrapper } = setUpWrapper({ pointsEarned: 300, activitiesLogged: 2});
expect(shallowWrapper.find('.profile-overview').debug()).toContain("<MyStatsComponent points={300} activities={2} />");
const { shallowWrapper } = setUpWrapper({ pointsEarned: 300, activitiesLogged: 2 });
expect(shallowWrapper.find('.profile-overview').debug())
.toContain('<MyStatsComponent points={300} activities={2} />');
});

it('should open the Log Points Modal', () => {
const { shallowWrapper } = setUpWrapper();
const instance = shallowWrapper.instance();
expect(instance.state.logPoints).toBe(false);
shallowWrapper.find('.button__add').simulate('click');
expect(instance.state.logPoints).toBe(true);
});
});
47 changes: 47 additions & 0 deletions src/app/Dashboard/components/tests/LogPointsModalConatiner.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { shallow } from 'enzyme';
import { LogPointsModal } from '../LogPointsModalContainer';
import { categories as activities } from '../../operations/tests/fixtures';

describe('<LogPointsComponent />', () => {
const setUpWrapper = ({
categories = activities,
} = {}) => {
const props = {
categories,
loadCategories: jest.fn(),
handleChange: jest.fn(),
};
const shallowWrapper = shallow(<LogPointsModal {...props} />);
return {
shallowWrapper,
};
};

it('should submit the log society activity points data', () => {
const { shallowWrapper } = setUpWrapper();
const instance = shallowWrapper.instance();
instance.setState({
logPoints: false,
});
instance.handleSubmit();
expect(instance.state.logPoints).toBe(false);
});

it('should handleChange on the Textfields', () => {
const { shallowWrapper } = setUpWrapper();
expect(shallowWrapper.find('#standard-description').simulate('change', { target: { value: 'Description' } }));
});

it('should contain Log in points text', () => {
const { shallowWrapper } = setUpWrapper();
expect(shallowWrapper.find('.log-points__heading').text()).toEqual('Log in points');
});

it('should call componentDidUpdate after state change', () => {
const { shallowWrapper } = setUpWrapper();
const instance = shallowWrapper.instance();
const categoryOption = 0;
expect(instance.setFormState(categoryOption, activities[0], categoryOption)).toMatchSnapshot();
});
});
Loading

0 comments on commit be9a821

Please sign in to comment.