Skip to content

Commit

Permalink
ft(Create Article): User should be able to create an article
Browse files Browse the repository at this point in the history
- Add user create article form.
- Add container for create article form.
- Add component for create article form.
- Add actions and reducers for create article.

[#161348758]
  • Loading branch information
ATUHAIRE JUDE INNOCENT committed Dec 20, 2018
1 parent 4a3fd35 commit dea500e
Show file tree
Hide file tree
Showing 24 changed files with 802 additions and 5 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
APP_URL=https://ah-backend-thanos-staging.herokuapp.com/api
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"react-share": "^2.4.0",
"react-toastify": "^4.4.3",
"react-twitter-auth": "^0.0.12",
"react-tagsinput": "^3.19.0",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.7",
"redux-logger": "^3.0.6",
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
<script src="https://widget.cloudinary.com/v2.0/global/all.js" type="text/javascript"></script>
</body>

</html>
</html>
4 changes: 4 additions & 0 deletions src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ exports[`Provider and App renders <Provider/> correctly 2`] = `
exact={true}
path="/newpassword"
/>
<Route
component={[Function]}
path="/createArticle"
/>
</Switch>
<_class
options={Object {}}
Expand Down
4 changes: 4 additions & 0 deletions src/actions/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const ACTION_TYPE = {
POST_RATING_SUCCESS: 'POST_RATING_SUCCESS',
POST_RATING_FAILED: 'POST_RATING_FAILED',
POST_RATING_DATA: 'POST_RATING_DATA',
CREATE_ARTICLE_SUCCESS: 'CREATE_ARTICLE_SUCCESS',
CREATE_ARTICLE_FAILED: 'CREATE_ARTICLE_FAILED',
POST_ARTICLE_DATA: 'POST_ARTICLE_DATA',
UPDATE_IMAGE_URL: 'UPDATE_IMAGE_URL',
};

export default ACTION_TYPE;
62 changes: 62 additions & 0 deletions src/actions/createArticleActions/createArticleActions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import moxios from 'moxios';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {
createArticleSuccess, createArticleThunk,
} from './index';
import ACTION_TYPE from '../actionTypes';
import APP_URL from '../../utils/constants';


describe('Create article Actions tests', () => {
let store;
let actionTypesData;
let response;
beforeEach(() => {
response = { title: 'going to school' };
const mockStore = configureMockStore([thunk]);
store = mockStore({});
actionTypesData = actionType => ({
type: actionType,
payload: response,
});
// import and pass your custom axios instance to this method
moxios.install();
});
afterEach(() => {
// import and pass your custom axios instance to this method
moxios.uninstall();
});
test('Successful get and post rating action', () => {
expect(createArticleSuccess(response)).toEqual(expect.objectContaining(
actionTypesData(ACTION_TYPE.CREATE_ARTICLE_SUCCESS),
));
});
test('Post article successfull', () => {
moxios.stubRequest(`${APP_URL}/articles`, {
status: 200,
response: { message: 'ok' },
});
store.dispatch(createArticleThunk()).then(() => {
expect(store.getActions()).toEqual(expect.objectContaining(
{
type: ACTION_TYPE.CREATE_ARTICLE_SUCCESS,
payload: { message: 'ok' },
},
));
});
});
test('Post article successfull', () => {
moxios.stubRequest(`${APP_URL}/articles`, {
status: 400,
responseText: { error: 'ok' },
});
store.dispatch(createArticleThunk()).then(() => {
expect(store.getActions()).toEqual(expect.objectContaining(
[{
type: ACTION_TYPE.CREATE_ARTICLE_FAILED,
}],
));
});
});
});
44 changes: 44 additions & 0 deletions src/actions/createArticleActions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from 'axios';
import ACTION_TYPE from '../actionTypes';
import APP_URL from '../../utils/constants';

export const postArticleData = response => ({
type: ACTION_TYPE.POST_ARTICLE_DATA,
payload: response,
});

export const createArticleSuccess = response => ({
type: ACTION_TYPE.CREATE_ARTICLE_SUCCESS,
payload: response,
});

export const createArticleFailure = errorMessage => ({
type: ACTION_TYPE.CREATE_ARTICLE_FAILED,
errorMessage,
});

export const updateImageUrl = imageUrl => ({
type: ACTION_TYPE.UPDATE_IMAGE_URL,
payload: imageUrl,
});

const userToken = localStorage.getItem('token');
export const createArticleThunk = data => (dispatch) => {
const userdata = {
...data,
};
const url = `${APP_URL}/articles`;
const token = `Token ${userToken}`;
const headers = {
headers: { Authorization: token },
};
return axios.post(url, userdata, headers)
.then((res) => {
const resData = res.data.results;
dispatch(createArticleSuccess(resData));
}).catch((error) => {
const errorData = error.response.data;
dispatch(createArticleFailure(errorData));
});
};
export default createArticleThunk;
6 changes: 6 additions & 0 deletions src/actions/swalAlerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ const swalMessages = {
type: 'success',
confirmButtonText: 'continue',
},
CREATE_ARTICLE_SUCCESSFUL: {
title: 'Article created',
text: 'Your article was successfully created',
type: 'success',
confirmButtonText: 'continue',
},
};

export default swalMessages;
1 change: 1 addition & 0 deletions src/commons/initialStates.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const initialState = {
},
socialLoginReducer: { isLoggedIn: false },
loginReducer: { errorMessage: '', successMessage: '', user_details: '' },
createArticleReducer: {},
userReducer: {
freshUser: { email: '', password: '', username: '' },
},
Expand Down
2 changes: 2 additions & 0 deletions src/components/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ProfileConnected from '../../containers/profiles/profiles';
import EditProfilePageConnected from '../../containers/profiles/editProfile';
import PasswordResetPage from '../../containers/PasswordResetPage';
import NewPasswordPage from '../../containers/PasswordResetPage/newpasswordPage';
import CreateArticlePage from '../../containers/CreateArticlePage';

library.add(faSearch);
const App = () => (
Expand All @@ -30,6 +31,7 @@ const App = () => (
<Route path="/profiles/edit" component={EditProfilePageConnected} />
<Route path="/passwordreset" component={PasswordResetPage} />
<Route path="/newpassword" component={NewPasswordPage} exact />
<Route path="/createArticle" component={CreateArticlePage} />
</Switch>
<Notification />
<FooterConnected />
Expand Down
50 changes: 50 additions & 0 deletions src/components/CreateArticle/CreateArticle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed');

#titleInput,
#descriptionInput,
#image_urlInput,
#tag_listInput {
height: 40px;
border-top: none;
border-left: none;
border-right: none;
}

#createArticleBt {
padding: 15px;
width: 30%;
background-color: #47d79f;
border: none;
font-family: "Roboto Condensed", sans-serif;
&:hover {
background-color: #3db184;
}

}
.artcileCreate{
font-family: "Roboto Condensed", sans-serif;
}
.createArticleTitle {
font-family: "Roboto Condensed", sans-serif;
}

.artcileCreate {
padding-bottom: 30px;
}
#bodyInput {
height: 500px;
}
.create-article-form {
width: 80%;
}

.image-upload{
color: turquoise !important;
border-color: turquoise !important; }

.image {
color: white;
}
input[type="file"] {
border-color: turquoise !important;
}
16 changes: 16 additions & 0 deletions src/components/CreateArticle/CreateArticle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { shallow } from 'enzyme';
import CreateArticle from './index';

describe('<CreateArticle /> ', () => {
const CreateArticleComponent = shallow(
<CreateArticle
onChange={jest.fn()}
onSubmit={jest.fn()}
onClick={jest.fn()}
/>,
);
test('renders the component', () => {
expect(CreateArticleComponent).toMatchSnapshot();
});
});
122 changes: 122 additions & 0 deletions src/components/CreateArticle/__snapshots__/CreateArticle.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<CreateArticle /> renders the component 1`] = `
<div>
<br />
<h3
className="text-center createArticleTitle"
>
Create Your Article
</h3>
<hr
className="col-sm-8 "
/>
<br />
<div
className="col-sm-10 offset-2 artcileCreate"
>
<form
className="create-article-form"
onSubmit={[MockFunction]}
>
<h5>
Title:
</h5>
<small>
Title must be 130 characters maximum and 5 characters minimum
</small>
<div
className="form-group"
>
<textarea
className="form-control"
id="titleInput"
maxLength="130"
minLength="5"
name="title"
onChange={[MockFunction]}
required={true}
rows="3"
/>
</div>
<h5>
Description
</h5>
<small>
Title must be 150 characters maximum and 100 characters minimum
</small>
<div
className="form-group"
>
<textarea
className="form-control"
id="descriptionInput"
maxLength="120"
minLength="100"
name="description"
onChange={[MockFunction]}
required={true}
rows="3"
/>
</div>
<h5>
Tag list
</h5>
<div
className="form-group"
>
<textarea
className="form-control"
id="tag_listInput"
name="tag_list"
onChange={[MockFunction]}
required={true}
rows="3"
/>
</div>
<br />
<h5>
Upload Image
</h5>
<div
className="col-lg-8 mt-2"
>
<input
className="image"
name="image_url"
onClick={[MockFunction]}
type="file"
/>
</div>
<br />
<h5>
Body
</h5>
<div
className="form-group"
>
<textarea
className="form-control"
id="bodyInput"
name="body"
onChange={[MockFunction]}
required={true}
rows="3"
/>
</div>
<div
className="signin-createArticleBt"
>
<button
className="btn btn-primary"
id="createArticleBt"
name="createArticleBt"
type="submit"
>
Publish Article
</button>
</div>
</form>
</div>
</div>
`;
Loading

0 comments on commit dea500e

Please sign in to comment.