diff --git a/App.js b/App.js
index 5fee225..8dec87e 100644
--- a/App.js
+++ b/App.js
@@ -12,7 +12,7 @@ import reducer from './src/reducers';
import HomeScreenContainer from './src/containers/HomeScreenContainer';
import SearchView from './src/components/HomeScreen/SearchView';
-import DetailScreen from './src/components/screens/DetailScreen';
+import DetailScreenContainer from './src/containers/DetailScreenContainer';
import EntryScreen from './src/components/screens/EntryScreen';
import ScanScreen from './src/components/screens/ScanScreen';
import EntryTagsScreen from './src/components/screens/EntryTagsScreen';
@@ -34,7 +34,7 @@ const RootStack = StackNavigator(
screen: EntryTagsScreen,
},
Detail: {
- screen: DetailScreen,
+ screen: DetailScreenContainer,
},
Search: {
screen: SearchView,
diff --git a/package.json b/package.json
index e054f19..610507a 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"test": "jest",
"test:watch": "npm test -- --watch",
"appr": "appr",
+ "postinstall": "rndebugger-open",
"debugger": "open 'rndebugger://set-debugger-loc?host=localhost&port=19001'"
},
"jest": {
diff --git a/src/__tests__/components/DetailScreen.test.js b/src/__tests__/components/DetailScreen.test.js
new file mode 100644
index 0000000..5ee0728
--- /dev/null
+++ b/src/__tests__/components/DetailScreen.test.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import DetailScreen from '../../components/DetailScreen';
+import _books from '../../api/books.json';
+
+describe('', () => {
+ it('正しくレンダリングされていること', () => {
+ const navigation = {
+ state: {
+ params: {
+ item: _books[0],
+ },
+ },
+ };
+ const wrapper = shallow();
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/src/__tests__/components/__snapshots__/DetailScreen.test.js.snap b/src/__tests__/components/__snapshots__/DetailScreen.test.js.snap
new file mode 100644
index 0000000..c9c733b
--- /dev/null
+++ b/src/__tests__/components/__snapshots__/DetailScreen.test.js.snap
@@ -0,0 +1,688 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` 正しくレンダリングされていること 1`] = `
+ShallowWrapper {
+ "length": 1,
+ Symbol(enzyme.__root__): [Circular],
+ Symbol(enzyme.__unrendered__): ,
+ Symbol(enzyme.__renderer__): Object {
+ "batchedUpdates": [Function],
+ "getNode": [Function],
+ "render": [Function],
+ "simulateEvent": [Function],
+ "unmount": [Function],
+ },
+ Symbol(enzyme.__node__): Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+
+ javascript
+
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+
+
+
+
+
+
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": "#fff",
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "uri": "none",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+ javascript
+
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+
+ ,
+
+
+
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "tags": Array [],
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+ javascript
+ ,
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ "justifyContent": "center",
+ "marginLeft": 50,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "javascript ",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "fontSize": 20,
+ "marginBottom": 10,
+ },
+ },
+ "ref": null,
+ "rendered": "javascript ",
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": Array [
+ "出版日:",
+ "2018-02-09T00:00:00Z",
+ ],
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": "#6E6E6E",
+ "fontSize": 14,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ "出版日:",
+ "2018-02-09T00:00:00Z",
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ "flexDirection": "row",
+ "marginBottom": 15,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "status": true,
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "navigate": undefined,
+ "status": true,
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Symbol(enzyme.__nodes__): Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+
+
+ javascript
+
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+
+
+
+
+
+
+ ,
+ ],
+ "style": Object {
+ "backgroundColor": "#fff",
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "uri": "none",
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+
+
+ javascript
+
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+
+ ,
+
+
+
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "tags": Array [],
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+
+ javascript
+ ,
+
+ 出版日:
+ 2018-02-09T00:00:00Z
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ "justifyContent": "center",
+ "marginLeft": 50,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": "javascript ",
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "fontSize": 20,
+ "marginBottom": 10,
+ },
+ },
+ "ref": null,
+ "rendered": "javascript ",
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "accessible": true,
+ "allowFontScaling": true,
+ "children": Array [
+ "出版日:",
+ "2018-02-09T00:00:00Z",
+ ],
+ "ellipsizeMode": "tail",
+ "style": Object {
+ "color": "#6E6E6E",
+ "fontSize": 14,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ "出版日:",
+ "2018-02-09T00:00:00Z",
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "class",
+ "props": Object {
+ "children": Array [
+ ,
+ ,
+ ],
+ "style": Object {
+ "flex": 1,
+ "flexDirection": "row",
+ "marginBottom": 15,
+ },
+ },
+ "ref": null,
+ "rendered": Array [
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "status": true,
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ Object {
+ "instance": null,
+ "key": undefined,
+ "nodeType": "function",
+ "props": Object {
+ "navigate": undefined,
+ "status": true,
+ },
+ "ref": null,
+ "rendered": null,
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ "type": [Function],
+ },
+ ],
+ Symbol(enzyme.__options__): Object {
+ "adapter": ReactSixteenAdapter {
+ "options": Object {
+ "enableComponentDidUpdateOnSetState": true,
+ },
+ },
+ },
+}
+`;
diff --git a/src/components/DetailScreen/BookImage.js b/src/components/DetailScreen/BookImage.js
new file mode 100644
index 0000000..f0604e1
--- /dev/null
+++ b/src/components/DetailScreen/BookImage.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ View,
+ Image,
+} from 'react-native';
+
+import { bookImage as styles } from './Styles';
+
+const BookImage = ({ uri }) => {
+ let imageUri = uri;
+ if (imageUri === 'none') {
+ imageUri = 'https://facebook.github.io/react/logo-og.png';
+ }
+
+ return (
+
+
+
+ );
+};
+
+BookImage.propTypes = {
+ uri: PropTypes.string.isRequired,
+};
+
+export default BookImage;
diff --git a/src/components/DetailScreen/LendingButton.js b/src/components/DetailScreen/LendingButton.js
new file mode 100644
index 0000000..bdd8990
--- /dev/null
+++ b/src/components/DetailScreen/LendingButton.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { View } from 'react-native';
+import { Button } from 'react-native-elements';
+import { MaterialCommunityIcons } from '@expo/vector-icons';
+
+import { lendingButton as styles } from './Styles';
+
+const LendButton = ({ navigate }) => (
+
+ }
+ title="返却"
+ titleStyle={{ fontWeight: '700' }}
+ buttonStyle={{ width: 100, height: 60, backgroundColor: '#cd5c5c' }}
+ iconContainerStyle={{ marginRight: 10 }}
+ onPress={() => navigate('LentScan', { action: 'return' })}
+ />
+
+);
+
+const BorrowButton = ({ navigate }) => (
+
+ }
+ title="貸出"
+ titleStyle={{ fontWeight: '700' }}
+ buttonStyle={{ width: 100, height: 60, backgroundColor: '#2e8b57' }}
+ iconContainerStyle={{ marginRight: 10 }}
+ onPress={() => navigate('LentScan', { action: 'lend' })}
+ />
+
+);
+
+const LendingButton = ({ status, navigate }) => {
+ if (status === true) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+LendingButton.propTyles = {
+ status: PropTypes.bool.isRequired,
+ navigate: PropTypes.object.isRequired,
+};
+
+export default LendingButton;
diff --git a/src/components/DetailScreen/StatusIcon.js b/src/components/DetailScreen/StatusIcon.js
new file mode 100644
index 0000000..2c6f19d
--- /dev/null
+++ b/src/components/DetailScreen/StatusIcon.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { View } from 'react-native';
+import {
+ Octicons,
+ MaterialCommunityIcons,
+} from '@expo/vector-icons';
+
+import { statusIcon as styles } from './Styles';
+
+const LendIcon = () => (
+
+
+
+);
+
+const BorrowIcon = () => (
+
+
+
+);
+
+const StatusIcon = ({ status }) => {
+ if (status === true) {
+ return ;
+ }
+
+ return ;
+};
+
+StatusIcon.propTyles = {
+ status: PropTypes.bool.isRequired,
+};
+
+export default StatusIcon;
diff --git a/src/components/DetailScreen/Styles.js b/src/components/DetailScreen/Styles.js
new file mode 100644
index 0000000..43cb665
--- /dev/null
+++ b/src/components/DetailScreen/Styles.js
@@ -0,0 +1,97 @@
+import {
+ StyleSheet,
+ Dimensions,
+} from 'react-native';
+
+const { width } = Dimensions.get('window');
+
+export const index = StyleSheet.create({
+ base: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ mainContainer: {
+ flex: 1,
+ },
+ titleContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ marginLeft: 50,
+ },
+ title: {
+ fontSize: 20,
+ marginBottom: 10,
+ },
+ dateContainer: {
+ flex: 0.4,
+ },
+ date: {
+ fontSize: 14,
+ color: '#6E6E6E',
+ },
+ statusContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ marginBottom: 15,
+ },
+ button: {
+ width: 70,
+ },
+});
+
+export const tagsList = StyleSheet.create({
+ tagContainer: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ flex: 0.5,
+ margin: 10,
+ },
+ tag: {
+ margin: 3,
+ paddingVertical: 8,
+ },
+ tagText: {
+ paddingRight: 5,
+ color: '#808080',
+ },
+});
+
+export const bookImage = StyleSheet.create({
+ base: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ imgContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ img: {
+ height: 300,
+ width,
+ },
+});
+
+export const statusIcon = StyleSheet.create({
+ base: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ status: {
+ flex: 1,
+ },
+});
+
+export const lendingButton = StyleSheet.create({
+ base: {
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ buttonContainer: {
+ flex: 1,
+ },
+});
diff --git a/src/components/DetailScreen/TagsList.js b/src/components/DetailScreen/TagsList.js
new file mode 100644
index 0000000..e8dede2
--- /dev/null
+++ b/src/components/DetailScreen/TagsList.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {
+ Text,
+ View,
+ ScrollView,
+} from 'react-native';
+
+import { icon } from '../../utils/Icons';
+import { tagsList as styles } from './Styles';
+
+const TagsList = ({ tags }) => {
+ const formatted = [];
+
+ for (let i = 0; i < tags.length; i += 1) {
+ const container = (
+
+
+ {icon(tags[i].name)}{tags[i].name}
+
+
+ );
+
+ formatted.push(container);
+ }
+
+ return (
+
+
+ {formatted}
+
+
+ );
+};
+
+TagsList.propTyles = {
+ tags: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ name: PropTypes.string.isRequired,
+ })),
+};
+
+export default TagsList;
diff --git a/src/components/DetailScreen/index.js b/src/components/DetailScreen/index.js
new file mode 100644
index 0000000..816db62
--- /dev/null
+++ b/src/components/DetailScreen/index.js
@@ -0,0 +1,61 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Text,
+ View,
+} from 'react-native';
+
+import { index as styles } from './Styles';
+import TagsList from './TagsList';
+import BookImage from './BookImage';
+import StatusIcon from './StatusIcon';
+import LendingButton from './LendingButton';
+
+class DetailScreen extends Component {
+ render() {
+ const { navigate } = this.props.navigation;
+ const {
+ tags,
+ status,
+ image,
+ title,
+ published_at,
+ } = this.props.book;
+
+ return (
+
+
+
+
+
+ {title}
+ 出版日:{published_at}
+
+
+
+
+
+
+
+ );
+ }
+}
+
+DetailScreen.navigationOptions = {
+ title: '詳細',
+};
+
+DetailScreen.propTypes = {
+ book: PropTypes.shape({
+ tags: PropTypes.array.isRequired,
+ status: PropTypes.bool.isRequired,
+ image: PropTypes.string.isRequired,
+ title: PropTypes.string.isRequired,
+ published_at: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
+export default DetailScreen;
diff --git a/src/components/HomeScreen/BookList.js b/src/components/HomeScreen/BookList.js
index cab7827..47b5c88 100644
--- a/src/components/HomeScreen/BookList.js
+++ b/src/components/HomeScreen/BookList.js
@@ -45,17 +45,18 @@ const renderTags = (tags) => {
);
};
-const renderItem = (item, navigation) => {
- const status = item.status
+const renderItem = (book, navigation) => {
+ const status = book.status
?
: ;
+ const { navigate } = navigation;
return (
navigation.navigate('Detail', { item })}
- title={item.title}
- subtitle={renderTags(item.tags)}
+ onPress={() => navigate('Detail', { book })}
+ title={book.title}
+ subtitle={renderTags(book.tags)}
subtitleNumberOfLines={1}
badge={{ element: status }}
/>
diff --git a/src/components/screens/DetailScreen.js b/src/components/screens/DetailScreen.js
deleted file mode 100644
index b3aeeee..0000000
--- a/src/components/screens/DetailScreen.js
+++ /dev/null
@@ -1,206 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import {
- StyleSheet,
- Text,
- View,
- ScrollView,
- Image,
- Dimensions,
-} from 'react-native';
-import { Button } from 'react-native-elements';
-import {
- MaterialCommunityIcons,
- Octicons,
-} from '@expo/vector-icons';
-
-import { icon } from '../../utils/Icons';
-
-export default class DetailScreen extends Component {
- static navigationOptions = {
- title: '詳細',
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- currentStatus: this.props.navigation.state.params.item.status
- };
- }
-
- renderImage = () => {
- let imageUri = this.props.navigation.state.params.item.image;
- if (imageUri === 'none') {
- imageUri = 'https://facebook.github.io/react/logo-og.png';
- }
-
- return (
-
-
-
- );
- };
-
- renderTags = () => {
- const { tags } = this.props.navigation.state.params.item;
- let formatted = [];
- let formattedTag;
-
- for (let tag of tags) {
- formattedTag = {icon(tag.name)}{tag.name};
- formatted.push(
-
- {formattedTag}
-
- );
- }
-
- return (
-
-
- {formatted}
-
-
- );
- };
-
- renderIcon = () => {
- if (this.state.currentStatus === true) {
- return (
-
-
-
- );
- }
- return (
-
-
-
- );
- };
-
- renderButton = () => {
- const { navigate } = this.props.navigation;
-
- if (this.state.currentStatus === true) {
- return (
-
- }
- title="返却"
- titleStyle={{ fontWeight: '700' }}
- buttonStyle={{ width: 100, height: 60, backgroundColor: '#cd5c5c' }}
- iconContainerStyle={{ marginRight: 10 }}
- onPress={() => navigate('LentScan', { action: 'return' })}
- />
-
- );
- }
- return (
-
- }
- title="貸出"
- titleStyle={{ fontWeight: '700' }}
- buttonStyle={{ width: 100, height: 60, backgroundColor: '#2e8b57' }}
- iconContainerStyle={{ marginRight: 10 }}
- onPress={() => navigate('LentScan', { action: 'lend' })}
- />
-
- );
- };
-
- render() {
- const { params } = this.props.navigation.state;
- const renderImage = this.renderImage();
- const renderTags = this.renderTags();
- const renderIcon = this.renderIcon();
- const renderButton = this.renderButton();
-
- return (
-
- {renderImage}
-
- {renderTags}
-
- {params.item.title}
- 出版日:{params.item.published_at}
-
-
- {renderIcon}
- {renderButton}
-
-
-
- );
- }
-}
-
-const { width } = Dimensions.get('window');
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
- },
- base: {
- justifyContent: 'center',
- alignItems: 'center',
- },
- imgContainer: {
- flex: 1,
- },
- img: {
- height: 300,
- width,
- },
- mainContainer: {
- flex: 1,
- },
- tagContainer: {
- flex: 0.5,
- margin: 10,
- },
- tag: {
- margin: 3,
- paddingVertical: 8,
- },
- tagText: {
- paddingRight: 5,
- color: '#808080',
- },
- titleContainer: {
- flex: 1,
- justifyContent: 'center',
- marginLeft: 50,
- },
- title: {
- fontSize: 20,
- marginBottom: 10,
- },
- dateContainer: {
- flex: 0.4,
- },
- date: {
- fontSize: 14,
- color: '#6E6E6E',
- },
- statusContainer: {
- flex: 1,
- flexDirection: 'row',
- marginBottom: 15,
- },
- status: {
- flex: 1,
- },
- buttonContainer: {
- flex: 1,
- },
- button: {
- width: 70,
- },
-});
diff --git a/src/containers/DetailScreenContainer.js b/src/containers/DetailScreenContainer.js
new file mode 100644
index 0000000..e70d9c4
--- /dev/null
+++ b/src/containers/DetailScreenContainer.js
@@ -0,0 +1,8 @@
+import { connect } from 'react-redux';
+import DetailScreen from '../components/DetailScreen';
+
+const mapStateToProps = (state, props) => ({
+ ...props.navigation.state.params,
+});
+
+export default connect(mapStateToProps)(DetailScreen);