Skip to content

Commit

Permalink
Add capability to select a pizza.
Browse files Browse the repository at this point in the history
  • Loading branch information
ihsw committed Feb 19, 2018
1 parent b609033 commit 7cabe02
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 19 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [x] Test coverage reporting
- [x] Automated unit testing
- [x] Automated coverage reporting
- [ ] Query pizza configurations on application initialization
- [x] Query pizza configurations on application initialization
- [ ] List pizza sizes for user to select
- [ ] List pizza toppings for user to select (after selecting a pizza)
- [ ] List cart
Expand Down
33 changes: 24 additions & 9 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,53 @@ import { Dispatch } from 'redux';
import { getPizzaSizes } from '../Api';
import {
REQUEST_PIZZA_SIZES,
RECEIVE_PIZZA_SIZES
RECEIVE_PIZZA_SIZES,
SELECT_PIZZA_SIZE
} from '../constants';
import { GetPizzaSizesData } from '../types';

export interface RequestPizzaSizes {
type: REQUEST_PIZZA_SIZES;
}

export interface ReceivePizzaSizes {
type: RECEIVE_PIZZA_SIZES;
data: GetPizzaSizesData;
}

export type FetchPizzaSizesAction = RequestPizzaSizes | ReceivePizzaSizes;

export const requestPizzaSizes = (): RequestPizzaSizes => {
return {
type: REQUEST_PIZZA_SIZES
};
};

export interface ReceivePizzaSizes {
type: RECEIVE_PIZZA_SIZES;
data: GetPizzaSizesData;
}

export const receivePizzaSizes = (data: GetPizzaSizesData): ReceivePizzaSizes => {
return {
type: RECEIVE_PIZZA_SIZES,
data
};
};

export type FetchPizzaSize = RequestPizzaSizes | ReceivePizzaSizes;

export const fetchPizzaSizes = () => {
return (dispatch: Dispatch<FetchPizzaSizesAction>) => {
return (dispatch: Dispatch<FetchPizzaSize>) => {
dispatch(requestPizzaSizes());
return getPizzaSizes()
.then((res) => dispatch(receivePizzaSizes(res)));
};
};

export interface SelectPizzaSize {
type: SELECT_PIZZA_SIZE;
index: number;
}

export const selectPizzaSize = (index: number): SelectPizzaSize => {
return {
type: SELECT_PIZZA_SIZE,
index
};
};

export type PizzaSizeAction = FetchPizzaSize | SelectPizzaSize;
27 changes: 27 additions & 0 deletions src/components/PizzaSizes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ describe('PizzaSizes', () => {
loading={false}
pizzaSizes={[]}
fetchPizzaSizes={() => { return; }}
selectPizzaSize={() => { return; }}
currentPizzaSize={null}
/>
));
expect(pizzaSizes.find('.greeting').text()).toEqual('Hello, Adrian');
Expand All @@ -28,6 +30,8 @@ describe('PizzaSizes', () => {
loading={false}
pizzaSizes={[]}
fetchPizzaSizes={spy}
selectPizzaSize={() => { return; }}
currentPizzaSize={null}
/>
));
expect(spy.calledOnce).toBe(true);
Expand All @@ -40,9 +44,32 @@ describe('PizzaSizes', () => {
loading={true}
pizzaSizes={[]}
fetchPizzaSizes={() => { return; }}
selectPizzaSize={() => { return; }}
currentPizzaSize={null}
/>
));
expect(pizzaSizes.find('.greeting').length).toEqual(0);
expect(pizzaSizes.find('.loading').length).toEqual(1);
});

it('Calls selectPizzaSize when clicking on a size', () => {
const spy = sinon.spy();
const pizzaSizes = shallow((
<PizzaSizes
name="Adrian"
loading={false}
pizzaSizes={[{
name: 'ayy',
basePrice: 9.99,
maxToppings: 1,
toppings: []
}]}
fetchPizzaSizes={() => { return; }}
selectPizzaSize={spy}
currentPizzaSize={null}
/>
));
pizzaSizes.find('.pizza-size button').first().simulate('click');
expect(spy.calledOnce).toBe(true);
});
});
29 changes: 29 additions & 0 deletions src/components/PizzaSizes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { PizzaSize } from '../types';
export interface StateProps {
loading: boolean;
pizzaSizes: PizzaSize[];
currentPizzaSize: PizzaSize | null;
}

export interface DispatchProps {
fetchPizzaSizes: () => void;
selectPizzaSize: (i: number) => void;
}

export interface OwnProps {
Expand All @@ -32,6 +34,29 @@ export class PizzaSizes extends React.Component<Props> {
this.props.fetchPizzaSizes();
}

renderSizeItem(size: PizzaSize, i: number) {
return (
<li
key={i}
className="pizza-size"
>
Size: <button type="button" onClick={() => this.props.selectPizzaSize(i)}>{size.name}</button>
</li>
);
}

renderSize(size: PizzaSize | null) {
if (!size) {
return <p>Please select a pizza size.</p>;
}

return (
<div>
<p>Current size: {size.name}</p>
</div>
);
}

render() {
if (this.props.loading) {
return <div className="loading">Loading...</div>;
Expand All @@ -41,6 +66,10 @@ export class PizzaSizes extends React.Component<Props> {
<div>
<p className="greeting">Hello, {this.props.name}</p>
<p className="pizza-sizes">There are {this.props.pizzaSizes.length} pizza sizes.</p>
<ul>
{this.props.pizzaSizes.map((size, i) => this.renderSizeItem(size, i))}
</ul>
{this.renderSize(this.props.currentPizzaSize)}
</div>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ export type REQUEST_PIZZA_SIZES = typeof REQUEST_PIZZA_SIZES;

export const RECEIVE_PIZZA_SIZES = 'RECEIVE_PIZZA_SIZES';
export type RECEIVE_PIZZA_SIZES = typeof RECEIVE_PIZZA_SIZES;

export const SELECT_PIZZA_SIZE = 'SELECT_PIZZA_SIZE';
export type SELECT_PIZZA_SIZE = typeof SELECT_PIZZA_SIZE;
16 changes: 11 additions & 5 deletions src/containers/PizzaSizes.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { connect, Dispatch } from 'react-redux';

import { PizzaSizes, StateProps, DispatchProps, OwnProps } from '../components/PizzaSizes';
import { FetchPizzaSizesAction, fetchPizzaSizes } from '../actions';
import { FetchPizzaSize, fetchPizzaSizes, selectPizzaSize } from '../actions';
import { StoreState } from '../types';

export const mapStateToProps = ({ loading, pizzaSizes }: StoreState): StateProps => {
export const mapStateToProps = ({ loading, pizzaSizes, currentSizeIndex }: StoreState): StateProps => {
let currentPizzaSize = null;
if (typeof currentSizeIndex !== 'undefined' && currentSizeIndex !== null) {
currentPizzaSize = pizzaSizes[currentSizeIndex];
}
return {
loading,
pizzaSizes
pizzaSizes,
currentPizzaSize: currentPizzaSize
};
};

export const mapDispatchToProps = (dispatch: Dispatch<FetchPizzaSizesAction>): DispatchProps => {
export const mapDispatchToProps = (dispatch: Dispatch<FetchPizzaSize>): DispatchProps => {
return {
fetchPizzaSizes: () => dispatch(fetchPizzaSizes())
fetchPizzaSizes: () => dispatch(fetchPizzaSizes()),
selectPizzaSize: (index: number) => dispatch(selectPizzaSize(index))
};
};

Expand Down
3 changes: 2 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import './index.css';

const preloadedState: StoreState = {
loading: false,
pizzaSizes: []
pizzaSizes: [],
currentSizeIndex: null
};
const store = createStore<StoreState>(pizzaSizes, preloadedState, applyMiddleware(thunk));

Expand Down
9 changes: 6 additions & 3 deletions src/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { FetchPizzaSizesAction } from '../actions';
import { PizzaSizeAction } from '../actions';
import {
REQUEST_PIZZA_SIZES,
RECEIVE_PIZZA_SIZES
RECEIVE_PIZZA_SIZES,
SELECT_PIZZA_SIZE
} from '../constants';
import { StoreState } from '../types';

export const pizzaSizes = (state: StoreState, action: FetchPizzaSizesAction): StoreState => {
export const pizzaSizes = (state: StoreState, action: PizzaSizeAction): StoreState => {
switch (action.type) {
case REQUEST_PIZZA_SIZES:
return { ...state, loading: true };
case RECEIVE_PIZZA_SIZES:
return { ...state, loading: false, pizzaSizes: action.data.pizzaSizes };
case SELECT_PIZZA_SIZE:
return { ...state, currentSizeIndex: action.index };
default:
return state;
}
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface StoreState {
pizzaSizes: PizzaSize[];
loading: boolean;
currentSizeIndex?: number | null;
}

export interface GetPizzaSizesData {
Expand Down

0 comments on commit 7cabe02

Please sign in to comment.