Skip to content

Commit 1f8ac29

Browse files
committed
Cart page styles
1 parent e30c130 commit 1f8ac29

File tree

6 files changed

+226
-38
lines changed

6 files changed

+226
-38
lines changed

__tests__/components/CategoriesHeader.spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import CategoriesHeader from '~/components/CategoriesHeader';
88
const mockStore = createStore();
99

1010
const INITIAL_STATE = {
11-
categories: {
11+
categories: {
1212
items: [
1313
{
1414
id: 1,
15-
title: 'T-shirt'
15+
title: 'T-shirt',
1616
},
1717
{
1818
id: 2,
19-
title: 'Shoes'
20-
}
19+
title: 'Shoes',
20+
},
2121
],
2222
currentId: 2,
23-
}
23+
},
2424
};
2525

2626
let wrapper;
@@ -37,7 +37,7 @@ describe('CategoriesHeader Component', () => {
3737
describe('Smoke tests', () => {
3838
it('Should render CategoriesHeader component correctly', () => {
3939
expect(wrapper.toJSON()).toMatchSnapshot();
40-
});
40+
});
4141
});
4242

4343
describe('Component structure', () => {

__tests__/components/ErrorMessage.spec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ import ErrorMessage from '~/components/ErrorMessage';
88
const mockStore = createStore();
99

1010
const INITIAL_STATE = {
11-
error: {
11+
error: {
1212
visible: true,
13-
message: "Message error test",
14-
}
13+
message: 'Message error test',
14+
},
1515
};
1616

1717
const INVISIBLE_STATE = {
18-
error: {
19-
visible: false,
20-
message: "Message error test",
21-
}
22-
};
18+
error: {
19+
visible: false,
20+
message: 'Message error test',
21+
},
22+
};
2323

2424
let wrapper;
2525
let invisibleWrapper;
@@ -30,7 +30,7 @@ beforeEach(() => {
3030
<ErrorMessage />
3131
</Provider>,
3232
);
33-
33+
3434
invisibleWrapper = ReactTestRender.create(
3535
<Provider store={mockStore(INVISIBLE_STATE)}>
3636
<ErrorMessage />
@@ -42,7 +42,7 @@ describe('ErrorMessage Component', () => {
4242
describe('Smoke tests', () => {
4343
it('Should render ErrorMessage component correctly', () => {
4444
expect(wrapper.toJSON()).toMatchSnapshot();
45-
});
45+
});
4646
});
4747

4848
describe('Component structure', () => {

__tests__/components/ProductsList.spec.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@ const INITIAL_STATE = {
1515
name: 'T-shirt',
1616
image: 'image test',
1717
brand: 'Brand test',
18-
price: 35.36
18+
price: 35.36,
1919
},
2020
{
2121
id: 2,
2222
name: 'Shoes',
2323
image: 'image teste 2',
2424
brand: 'Brand test 2',
25-
price: 25.36
25+
price: 25.36,
2626
},
2727
{
2828
id: 3,
2929
name: 'Shoes 2',
3030
image: 'image teste 3',
3131
brand: 'Brand test 3',
32-
price: 125.36
33-
}
32+
price: 125.36,
33+
},
3434
],
3535
},
3636
categories: {
3737
currentId: 3,
38-
}
38+
},
3939
};
4040

4141
const STATE_WITHOUT_ITEMS = {
@@ -44,8 +44,8 @@ const STATE_WITHOUT_ITEMS = {
4444
},
4545
categories: {
4646
currentId: 3,
47-
}
48-
}
47+
},
48+
};
4949

5050
const navigation = { navigate: jest.fn() };
5151

@@ -83,5 +83,5 @@ describe('ProductsList Component', () => {
8383
it('Should render no items', () => {
8484
expect(wrapperWithoutItems.root.findAllByType('TouchableOpacity').length).toBe(0);
8585
});
86-
});
86+
});
8787
});

src/pages/Cart/index.js

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,106 @@
1-
import React, { Component } from 'react';
1+
import React, { Component, Fragment } from 'react';
22
import { bindActionCreators } from 'redux';
33
import { connect } from 'react-redux';
4+
import { Alert } from 'react-native';
45
import PropTypes from 'prop-types';
56

67
import CartActions from '~/store/ducks/cart';
78

8-
import { Container, Product, Amount } from './styles';
9+
import { colors } from '~./styles';
10+
import {
11+
Container,
12+
CartList,
13+
CartItem,
14+
Image,
15+
Info,
16+
Name,
17+
Brand,
18+
Price,
19+
Form,
20+
AmountInput,
21+
DeleteButton,
22+
DeleteIcon,
23+
EmptyMessage,
24+
SubTotal,
25+
SubTotalText,
26+
SubTotalPrice,
27+
} from './styles';
928

1029
class Cart extends Component {
1130
static navigationOptions = {
1231
title: 'Cart',
13-
headerTitleStyle: { color: '#f19d9d' },
32+
headerTitleStyle: { color: colors.secondary },
33+
};
34+
35+
static propTypes = {
36+
removeItem: PropTypes.func.isRequired,
37+
changeItemQuantity: PropTypes.func.isRequired,
38+
amount: PropTypes.number.isRequired,
39+
items: PropTypes.arrayOf(
40+
PropTypes.shape({
41+
id: PropTypes.number.isRequired,
42+
image: PropTypes.string.isRequired,
43+
name: PropTypes.string.isRequired,
44+
brand: PropTypes.string.isRequired,
45+
price: PropTypes.number.isRequired,
46+
}),
47+
).isRequired,
48+
};
49+
50+
handleConfirmDelete = (product) => {
51+
const { removeItem } = this.props;
52+
53+
Alert.alert('Remove item', 'Are you sure you want to delete this item?', [
54+
{ text: 'Cancel' },
55+
{ text: 'Yes', onPress: () => removeItem(product.id) },
56+
]);
1457
};
1558

1659
render() {
17-
const { items } = this.props;
60+
const { items, amount, changeItemQuantity } = this.props;
1861

1962
return (
2063
<Container>
21-
{items.map(item => (
22-
<Product />
23-
))}
24-
25-
<Amount />
64+
{items.length > 0 ? (
65+
<Fragment>
66+
<CartList
67+
data={items}
68+
keyExtractor={product => String(product.id)}
69+
showsVerticalScrollIndicator={false}
70+
renderItem={({ item: product }) => (
71+
<CartItem key={product.id}>
72+
<Image source={{ uri: product.image }} />
73+
<Info>
74+
<Name>{product.name}</Name>
75+
<Brand>{product.brand}</Brand>
76+
<Price>{`$ ${product.price}`}</Price>
77+
</Info>
78+
<Form>
79+
<AmountInput
80+
autoCorrect={false}
81+
autoCapitalize="none"
82+
defaultValue={String(product.quantity)}
83+
maxLength={2}
84+
keyboardType="numeric"
85+
onChangeText={text => changeItemQuantity(product.id, Number(text))}
86+
>
87+
{product.amount}
88+
</AmountInput>
89+
<DeleteButton onPress={() => this.handleConfirmDelete(product)}>
90+
<DeleteIcon />
91+
</DeleteButton>
92+
</Form>
93+
</CartItem>
94+
)}
95+
/>
96+
<SubTotal>
97+
<SubTotalText>Total</SubTotalText>
98+
<SubTotalPrice>{`R$ ${amount.toFixed(2)}`}</SubTotalPrice>
99+
</SubTotal>
100+
</Fragment>
101+
) : (
102+
<EmptyMessage>There are no products in the cart.</EmptyMessage>
103+
)}
26104
</Container>
27105
);
28106
}

src/pages/Cart/styles.js

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,115 @@
11
import styled from 'styled-components/native';
22

3+
import { colors, metrics } from '~/styles';
4+
5+
import Icon from 'react-native-vector-icons/FontAwesome';
6+
37
export const Container = styled.View`
4-
background: #fff;
8+
flex: 1;
9+
background: ${colors.lighter};
10+
`;
11+
12+
export const CartList = styled.FlatList`
13+
padding: ${metrics.basePadding}px;
14+
`;
15+
16+
export const CartItem = styled.View`
17+
flex: 1;
18+
flex-direction: row;
19+
align-items: center;
20+
justify-content: space-between;
21+
margin-bottom: ${metrics.baseMargin}px;
22+
padding: ${metrics.basePadding}px;
23+
background: ${colors.white};
24+
border-radius: ${metrics.baseRadius}px;
25+
`;
26+
27+
export const Image = styled.Image.attrs({
28+
resizeMode: 'contain',
29+
})`
30+
width: 60px;
31+
height: 80px;
532
`;
633

7-
export const Product = styled.View``;
34+
export const Info = styled.View`
35+
flex: 1;
36+
margin: 0 ${metrics.baseMargin}px;
37+
`;
38+
39+
export const Name = styled.Text`
40+
font-size: 16px;
41+
color: ${colors.dark};
42+
font-weight: bold;
43+
`;
44+
45+
export const Brand = styled.Text`
46+
font-size: 12px;
47+
color: ${colors.gray};
48+
margin-top: 2px;
49+
`;
50+
51+
export const Price = styled.Text`
52+
font-size: 16px;
53+
color: ${colors.green};
54+
font-weight: bold;
55+
margin-top: ${metrics.baseMargin / 2}px;
56+
`;
857

9-
export const Amount = styled.View``;
58+
export const Form = styled.View`
59+
flex-direction: row;
60+
align-items: center;
61+
`;
62+
63+
export const AmountInput = styled.TextInput.attrs({
64+
underlineColorAndroid: 'transparent',
65+
})`
66+
width: 45px;
67+
height: 30px;
68+
padding: 0 ${metrics.baseMargin}px;
69+
padding-left: ${metrics.baseMargin}px;
70+
border-width: 1px;
71+
border-color: ${colors.gray};
72+
border-radius: ${metrics.baseRadius}px;
73+
font-weight: bold;
74+
color: ${colors.regular};
75+
`;
76+
77+
export const DeleteButton = styled.TouchableOpacity`
78+
margin-left: ${metrics.baseMargin}px;
79+
`;
80+
81+
export const DeleteIcon = styled(Icon).attrs({
82+
size: 20,
83+
color: colors.regular,
84+
name: 'times',
85+
})``;
86+
87+
export const EmptyMessage = styled.Text`
88+
margin-top: ${metrics.baseMargin * 2};
89+
font-size: 16px;
90+
text-align: center;
91+
color: ${colors.dark};
92+
font-weight: bold;
93+
`;
94+
95+
export const SubTotal = styled.View`
96+
height: 110px;
97+
background-color: ${colors.white};
98+
border-top-width: 3px;
99+
border-color: ${colors.lighter};
100+
align-items: center;
101+
justify-content: center;
102+
`;
103+
104+
export const SubTotalText = styled.Text`
105+
font-size: 14px;
106+
font-weight: bold;
107+
color: ${colors.gray};
108+
`;
109+
110+
export const SubTotalPrice = styled.Text`
111+
font-size: 24px;
112+
font-weight: bold;
113+
color: ${colors.green};
114+
margin-top: ${metrics.baseMargin}px;
115+
`;

src/pages/Product/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ class Product extends Component {
3838
};
3939

4040
handleAddToCart = (product) => {
41-
const { addItem } = this.props;
41+
const { addItem, navigation } = this.props;
4242

4343
addItem(product);
44+
navigation.navigate('Cart');
4445
};
4546

4647
render() {
@@ -73,4 +74,7 @@ class Product extends Component {
7374

7475
const mapDispatchToProps = dispatch => bindActionCreators(CartActions, dispatch);
7576

76-
export default connect(mapDispatchToProps)(Product);
77+
export default connect(
78+
null,
79+
mapDispatchToProps,
80+
)(Product);

0 commit comments

Comments
 (0)