diff --git a/_test_/App.spec.js b/_test_/App.spec.js index 55eee89..3682234 100644 --- a/_test_/App.spec.js +++ b/_test_/App.spec.js @@ -26,6 +26,6 @@ describe('App', () => { expect(app.find('Switch').length).toBe(1); }); it('renders a Route component', () => { - expect(app.find('Route').length).toBe(19); + expect(app.find('Route').length).toBe(15); }); }); diff --git a/_test_/ArticleActions.spec.js b/_test_/ArticleActions.spec.js index 2f6f601..90058fa 100644 --- a/_test_/ArticleActions.spec.js +++ b/_test_/ArticleActions.spec.js @@ -19,30 +19,32 @@ const store = makeMockStore({ }); describe('Article Actions', () => { - beforeEach(() => { - moxios.install(); - }); - afterEach(() => { - moxios.uninstall(); - }); - - it('fetches articles', () => { - moxios.wait(() => { - const request = moxios.requests.mostRecent(); - request.respondWith({ status: 200, response: fetchResponseData }); + describe('fetchArticles', () => { + beforeEach(() => { + moxios.install(); + }); + afterEach(() => { + moxios.uninstall(); }); - const expectedActions = [ - { type: SET_LOADING }, - { type: FETCH_ARTICLES, payload: mockData }, - { type: SET_CURRENT_ARTICLES, payload: mockData.articles }, - ]; - - return store.dispatch(fetchArticles()) - .then(() => { - const actionsCalled = store.getActions(); - expect(actionsCalled).toEqual(expectedActions); - expect(actionsCalled[1].type).toEqual(FETCH_ARTICLES); + it('fetches articles', () => { + moxios.wait(() => { + const request = moxios.requests.mostRecent(); + request.respondWith({ status: 200, response: fetchResponseData }); }); + + const expectedActions = [ + { type: SET_LOADING }, + { type: FETCH_ARTICLES, payload: mockData }, + { type: SET_CURRENT_ARTICLES, payload: mockData.articles }, + ]; + + return store.dispatch(fetchArticles()) + .then(() => { + const actionsCalled = store.getActions(); + expect(actionsCalled).toEqual(expectedActions); + expect(actionsCalled[1].type).toEqual(FETCH_ARTICLES); + }); + }); }); }); diff --git a/_test_/MainArticle.spec.js b/_test_/MainArticle.spec.js index 1839ed9..6a3111c 100644 --- a/_test_/MainArticle.spec.js +++ b/_test_/MainArticle.spec.js @@ -1,27 +1,100 @@ import React from 'react'; +import { BrowserRouter } from 'react-router-dom'; import expect from 'expect'; import { shallow } from 'enzyme'; -import MainArticle from '../src/components/SingleArticle/MainArticle'; +import ConnectMain, { MainArticle } from '../src/components/SingleArticle/MainArticle'; import image from '../src/assets/articleImage.png'; +import makeMockStore from './Utils/makeMockStore'; + +const store = makeMockStore({ + article: { + response: 'Article successfully deleted', + }, + err: { + error: 'error', + }, +}); describe('Main Article', () => { let component; + let app; + let mainApp; + let mainInstance; const props = { views: 1, imageUrl: '', + reponse: 'Article successfully deleted', + deleteArticle: jest.fn(), }; beforeEach(() => { - component = shallow(); + document.querySelectorAll = () => ['node']; + app = shallow( + + + , + ); + mainApp = shallow(); + mainInstance = mainApp.instance(); + }); + + it('has a toggleModal method', () => { + expect(mainInstance).toBeDefined(); + mainInstance.state = { + isSubmitted: true, + }; + mainInstance.toggleModal(); + mainInstance.handleSubmit(); + mainInstance.componentDidUpdate(); + }); + + it('sends an error response', () => { + const propsTwo = { + views: 1, + imageUrl: '', + deleteArticle: jest.fn(), + error: 'Request failed with status code 404', + response: '', + }; + const appTwo = shallow(); + const mainInst = appTwo.instance(); + mainInst.state = { + isSubmitted: true, + }; + expect(mainInstance).toBeDefined(); + mainInst.toggleModal(); + mainInst.handleSubmit(); + mainInst.componentDidUpdate(); + }); + + it('sends an success response', () => { + const propsTwo = { + views: 1, + imageUrl: '', + deleteArticle: jest.fn(), + error: '', + response: 'Article successfully deleted', + }; + const appTwo = shallow(); + const mainInst = appTwo.instance(); + mainInst.state = { + isSubmitted: true, + }; + expect(mainInstance).toBeDefined(); + mainInst.toggleModal(); + mainInst.handleSubmit(); + mainInst.componentDidUpdate(); }); it('renders successfully', () => { - expect(component).toBeDefined(); + const appComp = app.dive().dive().dive().dive(); + component = appComp.dive().dive().dive(); + expect(app).toBeDefined(); }); it('renders three div tags', () => { - expect(component.find('div').length).toBe(3); + expect(component.find('div').length).toBe(5); component.setProps({ views: 2 }); component.setProps({ imageUrl: image }); }); @@ -35,7 +108,7 @@ describe('Main Article', () => { }); it('renders a p tag', () => { - expect(component.find('p').length).toBe(3); + expect(component.find('p').length).toBe(4); }); it('renders a span tag', () => { diff --git a/_test_/deleteArticleAction.spec.js b/_test_/deleteArticleAction.spec.js new file mode 100644 index 0000000..ff26b37 --- /dev/null +++ b/_test_/deleteArticleAction.spec.js @@ -0,0 +1,81 @@ +import moxios from 'moxios'; +import axios from '../src/config/axiosInstance'; +import makeMockStore from './Utils/makeMockStore'; +import ArticleActions from '../src/actions/ArticleActions'; +import { + DELETE_ARTICLE_SUCCESS, GET_ERRORS, +} from '../src/actions/types'; + +const { deleteArticle } = ArticleActions; +const deleteReponse = { + status: 200, + message: 'Article successfully deleted', +}; +const baseUrl = 'https://ah-nyati-backend-staging.herokuapp.com/api/v1/articles/'; + +const store = makeMockStore({ + article: { + allArticles: [], + loading: false, + response: null, + }, + errors: {}, +}); + + +describe('Article Actions', () => { + describe('deleteArticle', () => { + beforeEach(() => { + moxios.install(); + }); + afterEach(() => { + moxios.uninstall(); + }); + + it('deletes an articles', () => { + moxios.wait(() => { + const request = moxios.requests.mostRecent(); + request.respondWith({ status: 200, response: deleteReponse }); + }); + + const expectedActions = [ + { type: DELETE_ARTICLE_SUCCESS, payload: deleteReponse.message }, + ]; + + return store.dispatch(deleteArticle()) + .then(() => { + const actionsCalled = store.getActions(); + expect(actionsCalled).toEqual(expectedActions); + expect(actionsCalled[0].type).toEqual(DELETE_ARTICLE_SUCCESS); + }); + }); + }); + + describe('deleteArticle error', () => { + beforeEach(() => { + moxios.install(axios); + }); + afterEach(() => { + moxios.uninstall(axios); + }); + + it('deletes an articles', () => { + moxios.stubRequest(`${baseUrl}article`, + { + status: 200, response: deleteReponse, + }); + + const expectedActions = [ + { type: DELETE_ARTICLE_SUCCESS, payload: deleteReponse.message }, + { type: GET_ERRORS, payload: 'Request failed with status code 401' }, + ]; + + return store.dispatch(deleteArticle()) + .then(() => { + const actionsCalled = store.getActions(); + expect(actionsCalled[0].type).toEqual(expectedActions[0].type); + expect(actionsCalled[1].type).toEqual(expectedActions[1].type); + }); + }); + }); +}); diff --git a/_test_/fetchAllArticleReducer.spec.js b/_test_/fetchAllArticleReducer.spec.js index 57434d5..01c9df0 100644 --- a/_test_/fetchAllArticleReducer.spec.js +++ b/_test_/fetchAllArticleReducer.spec.js @@ -3,6 +3,7 @@ import { FETCH_ARTICLES, SET_LOADING, SET_CURRENT_ARTICLES, + DELETE_ARTICLE_SUCCESS, } from '../src/actions/types'; describe('fetch all article reducer', () => { @@ -13,18 +14,27 @@ describe('fetch all article reducer', () => { currentArticles: [], allArticles: [], loading: false, + response: null, }, ); }); it('should fetch all articles loading state', () => { - expect(reducer(undefined, { type: FETCH_ARTICLES, payload: { articles: [], totalCount: 0 } })).toEqual( - { - totalArticles: 0, - currentArticles: [], - allArticles: [], - loading: false, + expect(reducer(undefined, { + type: FETCH_ARTICLES, + payload: { + articles: [], + totalCount: 0, }, - ); + })) + .toEqual( + { + totalArticles: 0, + currentArticles: [], + allArticles: [], + loading: false, + response: null, + }, + ); }); it('should set article loading state', () => { expect(reducer(undefined, { type: SET_LOADING, payload: { totalArticles: 0 } })).toEqual( @@ -33,9 +43,25 @@ describe('fetch all article reducer', () => { loading: true, currentArticles: [], allArticles: [], + response: null, }, ); }); + it('should delete an article', () => { + expect(reducer(undefined, { + type: DELETE_ARTICLE_SUCCESS, + payload: 'Article successfully deleted', + })) + .toEqual( + { + totalArticles: 0, + currentArticles: [], + allArticles: [], + loading: false, + response: 'Article successfully deleted', + }, + ); + }); it('should set current article loading state', () => { expect(reducer(undefined, { type: SET_CURRENT_ARTICLES, payload: [] })).toEqual( { @@ -43,6 +69,7 @@ describe('fetch all article reducer', () => { currentArticles: [], loading: false, allArticles: [], + response: null, }, ); }); diff --git a/package-lock.json b/package-lock.json index 3521097..12b8caf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5099,6 +5099,11 @@ } } }, + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -5805,28 +5810,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -5837,14 +5842,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -5855,42 +5860,42 @@ }, "chownr": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "resolved": false, "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "resolved": false, "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, @@ -5900,28 +5905,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -5931,14 +5936,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -5955,7 +5960,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "resolved": false, "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -5970,14 +5975,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -5987,7 +5992,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -5997,7 +6002,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -6008,21 +6013,21 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -6032,14 +6037,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -6049,14 +6054,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, @@ -6067,7 +6072,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -6077,7 +6082,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -6087,14 +6092,14 @@ }, "ms": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true, "optional": true }, "needle": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", + "resolved": false, "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, @@ -6106,7 +6111,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", + "resolved": false, "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, @@ -6125,7 +6130,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -6136,14 +6141,14 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "resolved": false, "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "resolved": false, "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, @@ -6154,7 +6159,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -6167,21 +6172,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -6191,21 +6196,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -6216,21 +6221,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -6243,7 +6248,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -6252,7 +6257,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -6268,7 +6273,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -6278,49 +6283,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "resolved": false, "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -6332,7 +6337,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -6342,7 +6347,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -6352,14 +6357,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "resolved": false, "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -6375,14 +6380,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -6392,14 +6397,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true @@ -10867,6 +10872,27 @@ "prop-types": "^15.6.2" } }, + "react-modal": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.10.1.tgz", + "integrity": "sha512-2DKIfdOc8+WY+SYJ/xf/WBwOYMmNAYAyGkYlc4e1TCs9rk1xY4QBz04hB3UHGcrLChh7ce77rHAe6VPNmuLYsQ==", + "requires": { + "exenv": "^1.2.0", + "prop-types": "^15.5.10", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "react-notify-toast": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/react-notify-toast/-/react-notify-toast-0.5.0.tgz", diff --git a/package.json b/package.json index 6836a11..37b20f6 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "react-infinite-scroller": "^1.2.4", "react-lazyload": "^2.6.2", "react-loader-spinner": "^3.1.2", + "react-modal": "^3.10.1", "react-notify-toast": "^0.5.0", "react-paginate": "^6.3.0", "react-redux": "^7.1.0", diff --git a/src/actions/ArticleActions.js b/src/actions/ArticleActions.js index 582a9cb..73d086b 100644 --- a/src/actions/ArticleActions.js +++ b/src/actions/ArticleActions.js @@ -5,14 +5,17 @@ import { GET_ERRORS, SET_CURRENT_ARTICLES, VIEW_SINGLE_ARTICLE, + DELETE_ARTICLE_SUCCESS, } from './types'; +const baseUrl = 'https://ah-nyati-backend-staging.herokuapp.com/api/v1/'; + class ArticleActions { static fetchArticles = (limit, page) => (dispatch) => { dispatch({ type: SET_LOADING, }); - return axios.get(`https://ah-nyati-backend-staging.herokuapp.com/api/v1/articles?limit=${limit}¤tPage=${page}`) + return axios.get(`${baseUrl}articles?limit=${limit}¤tPage=${page}`) .then((res) => { dispatch({ type: FETCH_ARTICLES, @@ -40,7 +43,7 @@ class ArticleActions { dispatch({ type: SET_LOADING, }); - return axios.get(`https://ah-nyati-backend-staging.herokuapp.com/api/v1/articles/${slug}`) + return axios.get(`${baseUrl}articles/${slug}`) .then((res) => { dispatch({ type: VIEW_SINGLE_ARTICLE, @@ -68,6 +71,24 @@ class ArticleActions { }); }); } + + static deleteArticle = slug => (dispatch) => { + const token = localStorage.getItem('jwtToken'); + const config = { headers: { token } }; + return axios.delete(`${baseUrl}articles/${slug}`, config) + .then((res) => { + dispatch({ + type: DELETE_ARTICLE_SUCCESS, + payload: res.data.message, + }); + }) + .catch((error) => { + return dispatch({ + type: GET_ERRORS, + payload: error.message, + }); + }); + }; } export default ArticleActions; diff --git a/src/actions/types.js b/src/actions/types.js index beda714..59b8501 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -19,6 +19,8 @@ export const CREATE_ARTICLE_START = 'CREATE_ARTICLE_START'; export const CREATE_ARTICLE_SUCCESS = 'CREATE_ARTICLE_SUCCESS'; export const CREATE_ARTICLE_FAILURE = 'CREATE_ARTICLE_FAILURE'; export const AUTH_LOADING = 'AUTH_LOADING'; +export const DELETE_ARTICLE_SUCCESS = 'DELETE_ARTICLE_SUCCESS'; +export const LOGIN_LOADING = 'LOGGIN_LOADING'; export const SUCCESS = 'SUCCESS'; export const SIGNUP_LOADING = 'SIGNUP_LOADING'; export const VIEW_SINGLE_ARTICLE = 'VIEW_SINGLE_ARTICLE'; diff --git a/src/components/App.js b/src/components/App.js index 9e423a8..dbde0f8 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -54,19 +54,15 @@ const App = () => ( - - + - - - diff --git a/src/components/ArticleCard/Index.jsx b/src/components/ArticleCard/Index.jsx index e979cba..27d23f9 100644 --- a/src/components/ArticleCard/Index.jsx +++ b/src/components/ArticleCard/Index.jsx @@ -52,16 +52,18 @@ ArticleCard.propTypes = { title: PropTypes.string.isRequired, text: PropTypes.string, date: PropTypes.string.isRequired, - imageUrl: PropTypes.string.isRequired, + imageUrl: PropTypes.string, readTime: PropTypes.string.isRequired, likeCount: PropTypes.number.isRequired, viewCount: PropTypes.number.isRequired, count: PropTypes.number.isRequired, - slug: PropTypes.string.isRequired, + slug: PropTypes.string, }; ArticleCard.defaultProps = { text: '', + imageUrl: '', + slug: '', }; export default ArticleCard; diff --git a/src/components/SingleArticle/MainArticle/MainArticle.scss b/src/components/SingleArticle/MainArticle/MainArticle.scss index 32ad6bc..fa138ee 100644 --- a/src/components/SingleArticle/MainArticle/MainArticle.scss +++ b/src/components/SingleArticle/MainArticle/MainArticle.scss @@ -2,6 +2,18 @@ margin-top: 40px; } +.editText { + text-align: center; + padding-top: 3px; + width: 70px; + height: 30px; + border-radius: 10px; + background-color: #2e99d6; + color: #fff; + font-size: 16px; + border: 1px solid #2e99d6 +} + .article-title { text-transform: capitalize; } @@ -25,6 +37,45 @@ border-left: 1.5px solid rgba(0, 0, 0, .4); } +.action-button-wrapper { + display: inline-block; + margin-top: 30px; +} + +.action-button { + margin-left: 10px; +} + +.del-btn { + background-color: #bd2935; + padding-top: 0; +} +.del-cancel-btn { + background-color: #9fb1bb; +} + +.del-confirm-btn { + background-color: #bd2935; +} + +.modal-text { + margin: 10px 0; + margin-bottom: 30px; +} + +.modal-btn-wrapper { + margin: 10px 0; +} + +.del-btn-grp { + padding: 0px; + border: unset; +} + +.del-btn-grp:focus { + outline: none; +} + .main-img { width: 100%; object-fit: cover; @@ -35,17 +86,6 @@ margin-top: 50px; font-size: 20px; } -.editText { - text-align: center; - padding-top: 3px; - width: 70px; - height: 30px; - border-radius: 10px; - background-color: #2e99d6; - color: #fff; - font-size: 16px; - border: 1px solid #2e99d6 -} @media (max-width: 1024px) { .main-img { diff --git a/src/components/SingleArticle/MainArticle/index.jsx b/src/components/SingleArticle/MainArticle/index.jsx index b71f65d..929bc17 100644 --- a/src/components/SingleArticle/MainArticle/index.jsx +++ b/src/components/SingleArticle/MainArticle/index.jsx @@ -1,91 +1,194 @@ -import React from 'react'; +import React, { Component } from 'react'; +import Modal from 'react-modal'; +import { connect } from 'react-redux'; +import { toastr } from 'react-redux-toastr'; import './MainArticle.scss'; -import { Link } from 'react-router-dom'; +import { Link, withRouter } from 'react-router-dom'; import PropTypes from 'prop-types'; import decodeToken from '../../../utils/decoded'; +import ArticleActions from '../../../actions/ArticleActions'; +import Helpers from '../../../helpers/helpers'; -const mainArticle = (props) => { - const { - title, articleBody, views, readTime, author, userId, slug, - } = props; +const { setAppElement } = Helpers; - let viewInfo = 'Views'; - const URL = `/profile/${userId}`; +setAppElement(process.env.NODE_ENV, Modal); - if (views < 2) { - viewInfo = 'View'; +const { deleteArticle } = ArticleActions; +const modalStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + }, +}; + +export class MainArticle extends Component { + constructor(props) { + super(props); + + this.state = { + isVisible: false, + isSubmitted: false, + }; } - let { imageUrl } = props; + componentDidUpdate() { + const { isSubmitted } = this.state; + const { response, error, history } = this.props; + const { goBack } = history; + if (isSubmitted && response === 'Article successfully deleted') { + toastr.success(response, 'taking you back to previous page...'); + this.setState(prevState => ({ isSubmitted: !prevState.isSubmitted })); + setTimeout(() => goBack(), 4000); + } else if (isSubmitted && error === 'Request failed with status code 404') { + toastr.success('Article not found', 'taking you back to previous page...'); + this.setState(prevState => ({ isSubmitted: !prevState.isSubmitted })); + setTimeout(() => goBack(), 4000); + } + } - const backupImage = 'https://i1.wp.com/www.africanbusinesscentral.com/wp-content/uploads/2018/07/Andela.jpeg'; + toggleModal = () => { + this.setState(prevState => ({ isVisible: !prevState.isVisible })); + } - if (imageUrl === '') { - imageUrl = backupImage; + handleSubmit = () => { + const { slug, deleteArticle: deleteOwnArticle } = this.props; + deleteOwnArticle(slug); + this.setState(prevState => ({ isSubmitted: !prevState.isSubmitted })); } - return ( -
-

{title}

-

- - {`${readTime} `} - + render() { + const { + title, articleBody, views, readTime, author, userId, slug, + } = this.props; + const { isVisible } = this.state; + + let viewInfo = 'Views'; + const URL = `/profile/${userId}`; + + if (views < 2) { + viewInfo = 'View'; + } + + let { imageUrl } = this.props; + + const backupImage = 'https://i1.wp.com/www.africanbusinesscentral.com/wp-content/uploads/2018/07/Andela.jpeg'; + + if (imageUrl === '') { + imageUrl = backupImage; + } + + return ( +

+

{title}

+

+ + {`${readTime} `} + min read -

- -

- - {views} - - {viewInfo} -

- - -

- - Author +

+ +

+ + {views} - {` ${author}`} + {viewInfo}

- - { + + +

+ + Author + + {` ${author}`} +

+ + { userId === decodeToken().id - && ( - - Edit - - ) - } - -
- article -
+ && ( +
+ + + + Edit + + +
+ ) + } -
- {articleBody} +
+ article +
+ +
+ {articleBody} +
+ +

Are you sure you want to delete this article?

+
+ + +
+
-
- ); -}; + ); + } +} -mainArticle.propTypes = { +MainArticle.propTypes = { title: PropTypes.string, articleBody: PropTypes.string, imageUrl: PropTypes.string, views: PropTypes.number, author: PropTypes.string, readTime: PropTypes.number, + deleteArticle: PropTypes.func.isRequired, userId: PropTypes.number.isRequired, + response: PropTypes.string, + error: PropTypes.string, + slug: PropTypes.string, + history: PropTypes.shape({ + goBack: PropTypes.func, + }), }; -mainArticle.defaultProps = { +MainArticle.defaultProps = { imageUrl: '', title: '', articleBody: '', views: 0, author: '', readTime: 0, + response: 'success', + error: 'error', + slug: 'valid-slug-84jfb4ur', + history: { + goBack: () => {}, + }, +}; + +const mapStateToProps = (state) => { + const { article, err } = state; + const { response } = article; + const { error } = err; + return { + response, + error, + }; }; -export default mainArticle; +export default withRouter(connect(mapStateToProps, { deleteArticle })(MainArticle)); diff --git a/src/helpers/helpers.js b/src/helpers/helpers.js index f27727b..abeef9b 100644 --- a/src/helpers/helpers.js +++ b/src/helpers/helpers.js @@ -93,6 +93,17 @@ class Helpers { history.push('/dashboard'); } } + + /** + * @memberof Helpers + * @static + * @description - redirects the page to dashboard + * @param {boolean} authFlag - authentication flag + * @param {object} history - the history object + */ + static setAppElement(env, Modal) { + if (env !== 'test') Modal.setAppElement('#root'); + } } export default Helpers; diff --git a/src/reducers/articleReducer.js b/src/reducers/articleReducer.js index 2da0fce..efa7de1 100644 --- a/src/reducers/articleReducer.js +++ b/src/reducers/articleReducer.js @@ -2,6 +2,7 @@ import { FETCH_ARTICLES, SET_LOADING, SET_CURRENT_ARTICLES, + DELETE_ARTICLE_SUCCESS, } from '../actions/types'; const initialState = { @@ -9,6 +10,7 @@ const initialState = { currentArticles: [], allArticles: [], loading: false, + response: null, }; const articleReducer = (state = initialState, action) => { @@ -29,6 +31,12 @@ const articleReducer = (state = initialState, action) => { loading: false, }; + case DELETE_ARTICLE_SUCCESS: + return { + ...state, + response: payload, + }; + case SET_LOADING: return { ...state,