From 8ba560e3b26f94c986630fd62ecd92ac1d2f1fe3 Mon Sep 17 00:00:00 2001 From: Vaillant Samuel Date: Wed, 13 Jun 2018 19:12:18 +0200 Subject: [PATCH] feat(templates): add React Native templates --- .../createInstantSearchApp.test.js.snap | 6 +- .../__snapshots__/e2e-templates.test.js.snap | 314 ++++++++++++++++++ .../.babelrc.template | 8 + .../React InstantSearch Native/.editorconfig | 9 + .../React InstantSearch Native/.eslintrc.js | 3 + .../.gitignore.template | 17 + .../React InstantSearch Native/.prettierrc | 5 + .../React InstantSearch Native/.template.js | 13 + .../.watchmanconfig | 1 + .../React InstantSearch Native/App.js.hbs | 53 +++ .../React InstantSearch Native/README.md | 19 ++ templates/React InstantSearch Native/app.json | 5 + .../React InstantSearch Native/package.json | 31 ++ .../src/Highlight.js | 36 ++ .../src/InfiniteHits.js.hbs | 44 +++ .../src/SearchBox.js.hbs | 34 ++ 16 files changed, 595 insertions(+), 3 deletions(-) create mode 100644 templates/React InstantSearch Native/.babelrc.template create mode 100644 templates/React InstantSearch Native/.editorconfig create mode 100644 templates/React InstantSearch Native/.eslintrc.js create mode 100644 templates/React InstantSearch Native/.gitignore.template create mode 100644 templates/React InstantSearch Native/.prettierrc create mode 100644 templates/React InstantSearch Native/.template.js create mode 100644 templates/React InstantSearch Native/.watchmanconfig create mode 100644 templates/React InstantSearch Native/App.js.hbs create mode 100644 templates/React InstantSearch Native/README.md create mode 100644 templates/React InstantSearch Native/app.json create mode 100644 templates/React InstantSearch Native/package.json create mode 100644 templates/React InstantSearch Native/src/Highlight.js create mode 100644 templates/React InstantSearch Native/src/InfiniteHits.js.hbs create mode 100644 templates/React InstantSearch Native/src/SearchBox.js.hbs diff --git a/packages/create-instantsearch-app/__snapshots__/createInstantSearchApp.test.js.snap b/packages/create-instantsearch-app/__snapshots__/createInstantSearchApp.test.js.snap index bc56d20ce..da837323a 100644 --- a/packages/create-instantsearch-app/__snapshots__/createInstantSearchApp.test.js.snap +++ b/packages/create-instantsearch-app/__snapshots__/createInstantSearchApp.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Options with unknown template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, Vue InstantSearch"`; +exports[`Options with unknown template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, React InstantSearch Native, Vue InstantSearch"`; exports[`Options with unvalid name throws 1`] = ` "Could not create a project called \\"./WrongNpmName\\" because of npm naming restrictions. @@ -8,8 +8,8 @@ exports[`Options with unvalid name throws 1`] = ` - name can only contain URL-friendly characters" `; -exports[`Options with wrong template path throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, Vue InstantSearch"`; +exports[`Options with wrong template path throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, React InstantSearch Native, Vue InstantSearch"`; exports[`Options without path throws 1`] = `"The option \`path\` is required."`; -exports[`Options without template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, Vue InstantSearch"`; +exports[`Options without template throws 1`] = `"The template directory must contain a configuration file \`.template.js\` or must be one of those: Angular InstantSearch, InstantSearch.js, React InstantSearch, React InstantSearch Native, Vue InstantSearch"`; diff --git a/scripts/__snapshots__/e2e-templates.test.js.snap b/scripts/__snapshots__/e2e-templates.test.js.snap index e16c6e300..a6d0b70ee 100644 --- a/scripts/__snapshots__/e2e-templates.test.js.snap +++ b/scripts/__snapshots__/e2e-templates.test.js.snap @@ -1610,6 +1610,320 @@ Array [ ] `; +exports[`Templates React InstantSearch Native File content: .babelrc 1`] = ` +"{ + \\"presets\\": [\\"babel-preset-expo\\"], + \\"env\\": { + \\"development\\": { + \\"plugins\\": [\\"transform-react-jsx-source\\"] + } + } +}" +`; + +exports[`Templates React InstantSearch Native File content: .editorconfig 1`] = ` +"root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true" +`; + +exports[`Templates React InstantSearch Native File content: .eslintrc.js 1`] = ` +"module.exports = { + extends: 'algolia/react', +};" +`; + +exports[`Templates React InstantSearch Native File content: .gitignore 1`] = ` +"# See https://help.github.com/ignore-files/ for more about ignoring files. + +# expo +.expo/ + +# dependencies +/node_modules + +# misc +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log*" +`; + +exports[`Templates React InstantSearch Native File content: .prettierrc 1`] = ` +"{ + \\"singleQuote\\": true, + \\"proseWrap\\": \\"never\\", + \\"trailingComma\\": \\"es5\\" +}" +`; + +exports[`Templates React InstantSearch Native File content: .watchmanconfig 1`] = `"{}"`; + +exports[`Templates React InstantSearch Native File content: App.js 1`] = ` +"import React from 'react'; +import { StyleSheet, View, SafeAreaView, StatusBar } from 'react-native'; +import algoliasearch from 'algoliasearch/reactnative'; +import { InstantSearch } from 'react-instantsearch-native'; +import SearchBox from './src/SearchBox'; +import InfiniteHits from './src/InfiniteHits'; + +const searchClient = algoliasearch( + 'appId', + 'apiKey' +); + +const styles = StyleSheet.create({ + safe: { + flex: 1, + backgroundColor: '#252b33', + }, + container: { + flex: 1, + backgroundColor: '#FFFFFF', + }, +}); + +class App extends React.Component { + root = { + Root: View, + props: { + style: { + flex: 1, + }, + }, + }; + + render() { + return ( + + + + + + + + + + ); + } +} + +export default App;" +`; + +exports[`Templates React InstantSearch Native File content: README.md 1`] = ` +"# react-instantsearch-native-app + +_This project was generated with [create-instantsearch-app](https://github.com/algolia/create-instantsearch-app) by [Algolia](https://algolia.com)._ + +## Get started + +To run this project locally, install the dependencies and run the local server: + +\`\`\`sh +npm install +npm start +\`\`\` + +Alternatively, you may use [Yarn](https://http://yarnpkg.com/): + +\`\`\`sh +yarn +yarn start +\`\`\`" +`; + +exports[`Templates React InstantSearch Native File content: app.json 1`] = ` +"{ + \\"expo\\": { + \\"sdkVersion\\": \\"27.0.0\\" + } +}" +`; + +exports[`Templates React InstantSearch Native File content: package.json 1`] = ` +"{ + \\"name\\": \\"react-instantsearch-native-app\\", + \\"version\\": \\"1.0.0\\", + \\"private\\": true, + \\"main\\": \\"./node_modules/react-native-scripts/build/bin/crna-entry.js\\", + \\"scripts\\": { + \\"start\\": \\"react-native-scripts start\\", + \\"android\\": \\"react-native-scripts android\\", + \\"ios\\": \\"react-native-scripts ios\\", + \\"lint\\": \\"eslint .\\", + \\"lint:fix\\": \\"npm run lint -- --fix\\" + }, + \\"dependencies\\": { + \\"algoliasearch\\": \\"^3.27.1\\", + \\"expo\\": \\"^27.0.1\\", + \\"prop-types\\": \\"^15.6.1\\", + \\"react\\": \\"16.3.1\\", + \\"react-instantsearch-native\\": \\"^1.0.0\\", + \\"react-native\\": \\"^0.55.2\\" + }, + \\"devDependencies\\": { + \\"eslint\\": \\"^4.19.1\\", + \\"eslint-config-algolia\\": \\"^13.1.0\\", + \\"eslint-config-prettier\\": \\"^2.9.0\\", + \\"eslint-plugin-import\\": \\"^2.12.0\\", + \\"eslint-plugin-prettier\\": \\"^2.6.0\\", + \\"eslint-plugin-react\\": \\"^7.9.1\\", + \\"prettier\\": \\"^1.13.4\\", + \\"react-native-scripts\\": \\"1.14.0\\" + } +}" +`; + +exports[`Templates React InstantSearch Native File content: src/Highlight.js 1`] = ` +"import React from 'react'; +import { Text } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectHighlight } from 'react-instantsearch-native'; + +const Highlight = ({ attribute, hit, highlight }) => { + const highlights = highlight({ + highlightProperty: '_highlightResult', + attribute, + hit, + }); + + return ( + + {highlights.map(({ value, isHighlighted }, index) => { + const style = { + backgroundColor: isHighlighted ? 'yellow' : 'transparent', + }; + + return ( + + {value} + + ); + })} + + ); +}; + +Highlight.propTypes = { + attribute: PropTypes.string.isRequired, + hit: PropTypes.object.isRequired, + highlight: PropTypes.func.isRequired, +}; + +export default connectHighlight(Highlight);" +`; + +exports[`Templates React InstantSearch Native File content: src/InfiniteHits.js 1`] = ` +"import React from 'react'; +import { StyleSheet, View, FlatList } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectInfiniteHits } from 'react-instantsearch-native'; +import Highlight from './Highlight'; + +const styles = StyleSheet.create({ + separator: { + borderBottomWidth: 1, + }, + item: { + padding: 10, + flexDirection: 'row', + alignItems: 'center', + }, +}); + +const InfiniteHits = ({ hits, hasMore, refine }) => ( + item.objectID} + ItemSeparatorComponent={() => } + onEndReached={() => hasMore && refine()} + renderItem={({ item }) => ( + + + + )} + /> +); + +InfiniteHits.propTypes = { + hits: PropTypes.arrayOf(PropTypes.object).isRequired, + hasMore: PropTypes.bool.isRequired, + refine: PropTypes.func.isRequired, +}; + +export default connectInfiniteHits(InfiniteHits);" +`; + +exports[`Templates React InstantSearch Native File content: src/SearchBox.js 1`] = ` +"import React from 'react'; +import { StyleSheet, View, TextInput } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectSearchBox } from 'react-instantsearch-native'; + +const styles = StyleSheet.create({ + container: { + padding: 10, + backgroundColor: '#252b33', + }, + input: { + height: 40, + padding: 10, + backgroundColor: '#FFFFFF', + }, +}); + +const SearchBox = ({ currentRefinement, refine }) => ( + + refine(value)} + value={currentRefinement} + placeholder=\\"Search placeholder\\" + /> + +); + +SearchBox.propTypes = { + currentRefinement: PropTypes.string.isRequired, + refine: PropTypes.func.isRequired, +}; + +export default connectSearchBox(SearchBox);" +`; + +exports[`Templates React InstantSearch Native Folder structure: contains the right files 1`] = ` +Array [ + ".babelrc", + ".editorconfig", + ".eslintrc.js", + ".gitignore", + ".prettierrc", + ".watchmanconfig", + "App.js", + "README.md", + "app.json", + "package.json", + "src/Highlight.js", + "src/InfiniteHits.js", + "src/SearchBox.js", +] +`; + exports[`Templates Vue InstantSearch File content: .babelrc 1`] = ` "{ \\"presets\\": [ diff --git a/templates/React InstantSearch Native/.babelrc.template b/templates/React InstantSearch Native/.babelrc.template new file mode 100644 index 000000000..2bcd546db --- /dev/null +++ b/templates/React InstantSearch Native/.babelrc.template @@ -0,0 +1,8 @@ +{ + "presets": ["babel-preset-expo"], + "env": { + "development": { + "plugins": ["transform-react-jsx-source"] + } + } +} diff --git a/templates/React InstantSearch Native/.editorconfig b/templates/React InstantSearch Native/.editorconfig new file mode 100644 index 000000000..9d08a1a82 --- /dev/null +++ b/templates/React InstantSearch Native/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/templates/React InstantSearch Native/.eslintrc.js b/templates/React InstantSearch Native/.eslintrc.js new file mode 100644 index 000000000..5b815c0d4 --- /dev/null +++ b/templates/React InstantSearch Native/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: 'algolia/react', +}; diff --git a/templates/React InstantSearch Native/.gitignore.template b/templates/React InstantSearch Native/.gitignore.template new file mode 100644 index 000000000..14a11718a --- /dev/null +++ b/templates/React InstantSearch Native/.gitignore.template @@ -0,0 +1,17 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# expo +.expo/ + +# dependencies +/node_modules + +# misc +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/templates/React InstantSearch Native/.prettierrc b/templates/React InstantSearch Native/.prettierrc new file mode 100644 index 000000000..833f03b62 --- /dev/null +++ b/templates/React InstantSearch Native/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "proseWrap": "never", + "trailingComma": "es5" +} diff --git a/templates/React InstantSearch Native/.template.js b/templates/React InstantSearch Native/.template.js new file mode 100644 index 000000000..c0decc17e --- /dev/null +++ b/templates/React InstantSearch Native/.template.js @@ -0,0 +1,13 @@ +const install = require('../../packages/tasks/node/install'); +const teardown = require('../../packages/tasks/node/teardown'); + +module.exports = { + libraryName: 'react-instantsearch-native', + templateName: 'react-instantsearch-native', + appName: 'react-instantsearch-native-app', + keywords: ['algolia', 'instantSearch', 'react', 'react-native', 'react-instantsearch-native'], + tasks: { + install, + teardown, + }, +}; diff --git a/templates/React InstantSearch Native/.watchmanconfig b/templates/React InstantSearch Native/.watchmanconfig new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/templates/React InstantSearch Native/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/templates/React InstantSearch Native/App.js.hbs b/templates/React InstantSearch Native/App.js.hbs new file mode 100644 index 000000000..01da44f9f --- /dev/null +++ b/templates/React InstantSearch Native/App.js.hbs @@ -0,0 +1,53 @@ +import React from 'react'; +import { StyleSheet, View, SafeAreaView, StatusBar } from 'react-native'; +import algoliasearch from 'algoliasearch/reactnative'; +import { InstantSearch } from 'react-instantsearch-native'; +import SearchBox from './src/SearchBox'; +import InfiniteHits from './src/InfiniteHits'; + +const searchClient = algoliasearch( + '{{appId}}', + '{{apiKey}}' +); + +const styles = StyleSheet.create({ + safe: { + flex: 1, + backgroundColor: '#252b33', + }, + container: { + flex: 1, + backgroundColor: '#FFFFFF', + }, +}); + +class App extends React.Component { + root = { + Root: View, + props: { + style: { + flex: 1, + }, + }, + }; + + render() { + return ( + + + + + + + + + + ); + } +} + +export default App; diff --git a/templates/React InstantSearch Native/README.md b/templates/React InstantSearch Native/README.md new file mode 100644 index 000000000..d50acaa49 --- /dev/null +++ b/templates/React InstantSearch Native/README.md @@ -0,0 +1,19 @@ +# {{name}} + +_This project was generated with [create-instantsearch-app](https://github.com/algolia/create-instantsearch-app) by [Algolia](https://algolia.com)._ + +## Get started + +To run this project locally, install the dependencies and run the local server: + +```sh +npm install +npm start +``` + +Alternatively, you may use [Yarn](https://http://yarnpkg.com/): + +```sh +yarn +yarn start +``` diff --git a/templates/React InstantSearch Native/app.json b/templates/React InstantSearch Native/app.json new file mode 100644 index 000000000..752f6170e --- /dev/null +++ b/templates/React InstantSearch Native/app.json @@ -0,0 +1,5 @@ +{ + "expo": { + "sdkVersion": "27.0.0" + } +} diff --git a/templates/React InstantSearch Native/package.json b/templates/React InstantSearch Native/package.json new file mode 100644 index 000000000..8deef9361 --- /dev/null +++ b/templates/React InstantSearch Native/package.json @@ -0,0 +1,31 @@ +{ + "name": "{{name}}", + "version": "1.0.0", + "private": true, + "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", + "scripts": { + "start": "react-native-scripts start", + "android": "react-native-scripts android", + "ios": "react-native-scripts ios", + "lint": "eslint .", + "lint:fix": "npm run lint -- --fix" + }, + "dependencies": { + "algoliasearch": "^3.27.1", + "expo": "^27.0.1", + "prop-types": "^15.6.1", + "react": "16.3.1", + "react-instantsearch-native": "^{{libraryVersion}}", + "react-native": "^0.55.2" + }, + "devDependencies": { + "eslint": "^4.19.1", + "eslint-config-algolia": "^13.1.0", + "eslint-config-prettier": "^2.9.0", + "eslint-plugin-import": "^2.12.0", + "eslint-plugin-prettier": "^2.6.0", + "eslint-plugin-react": "^7.9.1", + "prettier": "^1.13.4", + "react-native-scripts": "1.14.0" + } +} diff --git a/templates/React InstantSearch Native/src/Highlight.js b/templates/React InstantSearch Native/src/Highlight.js new file mode 100644 index 000000000..aefc156ff --- /dev/null +++ b/templates/React InstantSearch Native/src/Highlight.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { Text } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectHighlight } from 'react-instantsearch-native'; + +const Highlight = ({ attribute, hit, highlight }) => { + const highlights = highlight({ + highlightProperty: '_highlightResult', + attribute, + hit, + }); + + return ( + + {highlights.map(({ value, isHighlighted }, index) => { + const style = { + backgroundColor: isHighlighted ? 'yellow' : 'transparent', + }; + + return ( + + {value} + + ); + })} + + ); +}; + +Highlight.propTypes = { + attribute: PropTypes.string.isRequired, + hit: PropTypes.object.isRequired, + highlight: PropTypes.func.isRequired, +}; + +export default connectHighlight(Highlight); diff --git a/templates/React InstantSearch Native/src/InfiniteHits.js.hbs b/templates/React InstantSearch Native/src/InfiniteHits.js.hbs new file mode 100644 index 000000000..5cd7ea1c1 --- /dev/null +++ b/templates/React InstantSearch Native/src/InfiniteHits.js.hbs @@ -0,0 +1,44 @@ +import React from 'react'; +import { StyleSheet, {{#unless mainAttribute}}Text, {{/unless}}View, FlatList } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectInfiniteHits } from 'react-instantsearch-native'; +{{#if mainAttribute}} +import Highlight from './Highlight'; +{{/if}} + +const styles = StyleSheet.create({ + separator: { + borderBottomWidth: 1, + }, + item: { + padding: 10, + flexDirection: 'row', + alignItems: 'center', + }, +}); + +const InfiniteHits = ({ hits, hasMore, refine }) => ( + item.objectID} + ItemSeparatorComponent={() => } + onEndReached={() => hasMore && refine()} + renderItem={({ item }) => ( + + {{#if mainAttribute}} + + {{else}} + {JSON.stringify(item).slice(0, 100)} + {{/if}} + + )} + /> +); + +InfiniteHits.propTypes = { + hits: PropTypes.arrayOf(PropTypes.object).isRequired, + hasMore: PropTypes.bool.isRequired, + refine: PropTypes.func.isRequired, +}; + +export default connectInfiniteHits(InfiniteHits); diff --git a/templates/React InstantSearch Native/src/SearchBox.js.hbs b/templates/React InstantSearch Native/src/SearchBox.js.hbs new file mode 100644 index 000000000..12ad96c83 --- /dev/null +++ b/templates/React InstantSearch Native/src/SearchBox.js.hbs @@ -0,0 +1,34 @@ +import React from 'react'; +import { StyleSheet, View, TextInput } from 'react-native'; +import PropTypes from 'prop-types'; +import { connectSearchBox } from 'react-instantsearch-native'; + +const styles = StyleSheet.create({ + container: { + padding: 10, + backgroundColor: '#252b33', + }, + input: { + height: 40, + padding: 10, + backgroundColor: '#FFFFFF', + }, +}); + +const SearchBox = ({ currentRefinement, refine }) => ( + + refine(value)} + value={currentRefinement} + placeholder="{{searchPlaceholder}}" + /> + +); + +SearchBox.propTypes = { + currentRefinement: PropTypes.string.isRequired, + refine: PropTypes.func.isRequired, +}; + +export default connectSearchBox(SearchBox);