Skip to content

Commit

Permalink
feature(delete-book): add delete book confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
segunolalive committed Dec 10, 2017
1 parent 25efe44 commit e0a2994
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 24 deletions.
29 changes: 29 additions & 0 deletions client/__tests__/components/common/Modal.spec.jsx
@@ -0,0 +1,29 @@
import React from 'react';
import { shallow } from 'enzyme';

import Modal from "../../../components/common/Modal";

const props = {
title: 'title',
question: 'question',
subText: 'subText',
confirmColor: 'confirmColor',
cancelColor: 'cancelColor',
confirmText: 'confirmText',
cancelText: 'cancelText',
modalAction: jest.fn()
};

describe('Modal', () => {
const wrapper = shallow(<Modal { ...props } />);
it('renders without crashing', () => {
expect(wrapper.getElement().type).toBe("div");
expect(wrapper.find("div").length).toBeGreaterThan(1);
});

it("calls modalAction prop function when a confirm button is clicked", () => {
const confirmButton = wrapper.find("button").at(1);
confirmButton.simulate("click");
expect(wrapper.instance().props.modalAction).toHaveBeenCalled();
});
})
3 changes: 2 additions & 1 deletion client/__tests__/reducers/bookReducer.spec.js
Expand Up @@ -126,8 +126,9 @@ describe('Book Reducer', () => {

it('should handle actions of type DELETE_BOOK', () => {
action = deleteBookAction(5);
expect(initialState.bookReducer.books.length).toBe(1);
newState = bookReducer(initialState.bookReducer, action);
expect(newState).not.toEqual(initialState.bookReducer);
expect(newState.length).toBe(0);
expect(newState.books.length).toBe(0);
});
});
18 changes: 18 additions & 0 deletions client/__tests__/reducers/rootReducer.spec.js
@@ -0,0 +1,18 @@
import rootReducer from "../../reducers/rootReducer";
import initialState from "../../reducers/initialState";
import { logoutUser } from "../../actions/authActions/logout";


describe("Root Reducer", () => {
it("should return initial state for unkwon action types", () => {
const action = { type: null };
const newState = rootReducer(initialState, action);
expect(newState).toEqual(initialState);
});

it("handles action of type LOGOUT", () => {
const action = logoutUser();
const newState = rootReducer(initialState, action);
expect(newState).toEqual(initialState);
});
});
40 changes: 20 additions & 20 deletions client/components/Library/BookDetail.jsx
Expand Up @@ -5,11 +5,10 @@ import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';

import Header from '../Header';
import Modal from '../common/Modal';
import { borrowBook } from '../../actions/bookActions/library';
import { deleteBook } from '../../actions/adminActions/books';
import { viewBookDetails } from '../../actions/bookActions/viewBook';
import notify from '../../actions/notify';
import { loadavg } from 'os';

/**
* displays book details
Expand All @@ -18,19 +17,7 @@ import { loadavg } from 'os';
* @extends {Component}
*/
export class BookDetail extends Component {
/**
* Creates an instance of BookDetail.
* @param {any} props
* @memberof BookDetail
*/
constructor(props) {
super(props);
this.state = {};
this.handleBorrow = this.handleBorrow.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.handleRedirect = this.handleRedirect.bind(this);
}
state = {}

/**
* lifecycle hook invoked once component is mounted to DOM
Expand All @@ -48,7 +35,7 @@ export class BookDetail extends Component {
* @memberof BookDetail
* @returns {Undefined} sends request to borrow book
*/
handleBorrow() {
handleBorrow = () => {
this.props.borrowBook(this.props.userId, this.props.book.id);
}

Expand All @@ -58,15 +45,15 @@ export class BookDetail extends Component {
* @memberof BookDetail
* @returns {Undefined} redirects to edit book page
*/
handleEditClick() {
handleEditClick= () => {
this.setState({ editRedirect: true });
}
/**
* handler for delete book button click
* @memberof BookDetail
* @returns {Undefined} deletes a book from library
*/
handleDelete() {
handleDelete = () => {
this.props.deleteBook(this.props.book.id)
.then(() => this.setState({ deleteRedirect: true }));
}
Expand All @@ -76,7 +63,7 @@ export class BookDetail extends Component {
* @memberof BookDetail
* @returns {JSX | null} react-router-dom redirect element or null
*/
handleRedirect() {
handleRedirect = () => {
if (this.state.editRedirect) {
return <Redirect to='/admin/edit' />;
} else if (this.state.deleteRedirect) {
Expand All @@ -101,9 +88,10 @@ export class BookDetail extends Component {
Edit
</Button>
<Button
data-target="confirm-modal"
className="red darken-4 action-btn delete-btn"
waves="light"
onClick={this.handleDelete}
// onClick={this.handleDelete}
>
Delete
</Button>
Expand Down Expand Up @@ -151,6 +139,18 @@ export class BookDetail extends Component {
</div>
</div>
</section>
<Modal
title="Confirm Delete"
question={
`Are you sure you want to delete ${this.props.book &&
this.props.book.title}?`}
subText='This action cannot be reversed'
confirmText="Yes, Delete"
cancelText="No, cancel"
confirmColor="red"
cancelColor="teal"
modalAction={this.handleDelete}
/>
</div>
);
}
Expand Down
66 changes: 66 additions & 0 deletions client/components/common/Modal.jsx
@@ -0,0 +1,66 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import setupModal from './setupModal';


/**
* a modal Component
* @extends Component
*/
class Modal extends Component {
/**
* lifecycle method called when component mounts the DOM
* @returns {undefined} no return value
*/
componentDidMount() {
setupModal();
}

/**
* renders a Modal to the DOM
* @return {JSX} JSX representation of the Component
*/
render() {
return <div id="confirm-modal" className="modal admin-form">
<div className="modal-content center">
<h4 className="blue-border-bottom bold-text">{this.props.title}</h4>
<h5 className="grey-text text-lighten-2">{this.props.question}</h5>
<p className="bold-text">
{this.props.subText}
</p>
</div>
<div className="modal-footer admin-form center s12">
<div className="col s6 offset-s3">
<button
className={`modal-action btn darken-4 white-text modal-close
s6 action-btn waves-effect waves-red btn-flat
${this.props.confirmColor}`}
onClick={() => this.props.modalAction()}
>
{this.props.confirmText}
</button>
<button
className={`modal-action action-btn btn darken-4
white-text modal-close waves-effect
waves-green btn-flat ${this.props.cancelColor}`}
>
{this.props.cancelText}
</button>
</div>
</div>
</div>;
}
}

Modal.propTypes = {
title: PropTypes.string.isRequired,
question: PropTypes.string.isRequired,
subText: PropTypes.string.isRequired,
confirmColor: PropTypes.string.isRequired,
cancelColor: PropTypes.string.isRequired,
confirmText: PropTypes.string.isRequired,
cancelText: PropTypes.string.isRequired,
modalAction: PropTypes.func.isRequired
};

export default Modal;
6 changes: 6 additions & 0 deletions client/components/common/setupModal.js
@@ -0,0 +1,6 @@
export default () => {
const $ = window.$ || window.jquery;
$(document).ready(() => {
$('.modal').modal();
});
};
3 changes: 2 additions & 1 deletion client/reducers/bookReducer.js
Expand Up @@ -35,7 +35,8 @@ const bookReducer = (state = initialState.bookReducer, action) => {
case actionTypes.GET_BOOK_CATEGORIES:
return { ...state, categories: action.categories };
case actionTypes.DELETE_BOOK:
return state.books.filter(book => book.id !== action.id);
books = state.books.filter(book => book.id !== action.id);
return { ...state, books };
case actionTypes.SET_LIBRARY_PAGINATION:
return { ...state, pagination: action.pagination };
default:
Expand Down
19 changes: 17 additions & 2 deletions client/static/index.scss
Expand Up @@ -65,6 +65,9 @@ nav {
}
}

.blue-border-bottom {
border-bottom: $cool-Blue-Border;
}

.navbar-blue {
background: $theme-blue !important;
Expand Down Expand Up @@ -176,7 +179,7 @@ main {
}

.action-btn {
margin: 5px;
margin: 5px !important;
}

.borrowed-list {
Expand Down Expand Up @@ -224,7 +227,15 @@ input[type=search]:not(.browser-default) {
}
}

.search-btn:hover {
.select-wrapper:hover {
.caret {
color: #dddddd;
transform: scale(2) !important;
transition: all 0.5s ease-in-out;
}
}

.search-btn:hover, .search-btn-wrapper:hover {
background: transparent;
box-shadow: none;
i {
Expand Down Expand Up @@ -279,3 +290,7 @@ input[type=search]:not(.browser-default) {

}
}

.modal-footer {
background-color: transparent !important;
}

0 comments on commit e0a2994

Please sign in to comment.