From 6a00b3df02b01b14a05f897c5457dc3d9f5a9b0a Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Wed, 23 Aug 2017 17:51:28 +1000 Subject: [PATCH 01/61] ui,examples: add AutocompleteDropdown, add example to match --- examples/autocomplete-search/package.json | 19 ++ .../autocomplete-search/public/favicon.ico | Bin 0 -> 24838 bytes .../autocomplete-search/public/index.html | 30 +++ examples/autocomplete-search/src/App.js | 37 +++ examples/autocomplete-search/src/index.js | 6 + src/ui/classnames.js | 10 + src/ui/text/AutocompleteDropdown.css | 40 +++ src/ui/text/AutocompleteDropdown.js | 243 ++++++++++++++++++ 8 files changed, 385 insertions(+) create mode 100644 examples/autocomplete-search/package.json create mode 100644 examples/autocomplete-search/public/favicon.ico create mode 100644 examples/autocomplete-search/public/index.html create mode 100644 examples/autocomplete-search/src/App.js create mode 100644 examples/autocomplete-search/src/index.js create mode 100644 src/ui/classnames.js create mode 100644 src/ui/text/AutocompleteDropdown.css create mode 100644 src/ui/text/AutocompleteDropdown.js diff --git a/examples/autocomplete-search/package.json b/examples/autocomplete-search/package.json new file mode 100644 index 000000000..efca67330 --- /dev/null +++ b/examples/autocomplete-search/package.json @@ -0,0 +1,19 @@ +{ + "name": "autocomplete-search", + "version": "0.1.0", + "private": true, + "dependencies": { + "react": "15.6.1", + "react-dom": "15.6.1", + "sajari-react": "1.3.4" + }, + "devDependencies": { + "react-scripts": "1.0.11" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/examples/autocomplete-search/public/favicon.ico b/examples/autocomplete-search/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c125de5d897c1ff5692a656485b3216123dcd89 GIT binary patch literal 24838 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 + + + + + + Example: Autocomplete Search + + + + + +
+ + + diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-search/src/App.js new file mode 100644 index 000000000..d6b159c69 --- /dev/null +++ b/examples/autocomplete-search/src/App.js @@ -0,0 +1,37 @@ +import React from "react"; + +import { Pipeline, Values } from "sajari-react/controllers"; +import { Response, Results, Summary, Paginator } from "sajari-react/ui/results"; +import AutocompleteDropdown from "sajari-react/ui/text/AutocompleteDropdown"; + +import "sajari-react/ui/text/AutocompleteInput.css"; +import "sajari-react/ui/text/AutocompleteDropdown.css"; +import "sajari-react/ui/results/Results.css"; +import "sajari-react/ui/results/Paginator.css"; + +const project = "sajariptyltd"; +const collection = "sajari-com"; +const autocompletePipeline = new Pipeline(project, collection, "autocomplete"); +const websitePipeline = new Pipeline(project, collection, "website"); +const autocompleteValues = new Values(); +const websiteValues = new Values(); + +const App = () => +
+ { + websiteValues.set({ q: query, "q.override": true }); + websitePipeline.search(websiteValues.get()); + }} + /> + + + + + +
; + +export default App; diff --git a/examples/autocomplete-search/src/index.js b/examples/autocomplete-search/src/index.js new file mode 100644 index 000000000..1ccdf637e --- /dev/null +++ b/examples/autocomplete-search/src/index.js @@ -0,0 +1,6 @@ +import React from "react"; +import ReactDOM from "react-dom"; + +import App from "./App"; + +ReactDOM.render(, document.getElementById("root")); diff --git a/src/ui/classnames.js b/src/ui/classnames.js new file mode 100644 index 000000000..1d7618f85 --- /dev/null +++ b/src/ui/classnames.js @@ -0,0 +1,10 @@ +/** + * Produces a css class string from a class name to boolean value object map. + * Only class names whose values evaluate to true will be included. + * @param {Object} classes + * @return {string} + */ +const classnames = classes => + Object.keys(classes).filter(key => classes[key]).join(" "); + +export default classnames; diff --git a/src/ui/text/AutocompleteDropdown.css b/src/ui/text/AutocompleteDropdown.css new file mode 100644 index 000000000..1550b4940 --- /dev/null +++ b/src/ui/text/AutocompleteDropdown.css @@ -0,0 +1,40 @@ +.sj-suggestions { + position: absolute; + width: 100%; +} + +.sj-autocomplete-dropdown { + position: relative; +} + +.sj-search-bar-input { + position: static; + background-color: white; +} + +.sj-suggestion { + font-size: 18px; + padding: 8px 8px; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + background-color: #fff; + color: #666; +} + +.sj-suggestion:first-child { + border-top: 1px solid #ddd; +} + +.sj-suggestion:last-child { + border-bottom: 1px solid #ddd; +} + +.sj-suggestion strong { + font-weight: 600; + color: #333; +} + +.sj-suggestion.sj-suggestion-selected, +.sj-suggestion:hover { + background-color: #ddd; +} diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js new file mode 100644 index 000000000..2e2384ad6 --- /dev/null +++ b/src/ui/text/AutocompleteDropdown.js @@ -0,0 +1,243 @@ +import React from "react"; +import PropTypes from "prop-types"; + +import { + Pipeline, + Values, + responseUpdatedEvent, + valuesUpdatedEvent +} from "../../controllers"; + +import classnames from "../classnames"; + +const RIGHT_ARROW_KEYCODE = 39; +const TAB_KEYCODE = 9; +const RETURN_KEYCODE = 13; +const ESC_KEYCODE = 27; +const UP_ARROW_KEYCODE = 38; +const DOWN_ARROW_KEYCODE = 40; + +class AutocompleteSuggestion extends React.Component { + /** + * propTypes + * @property {string} text Current user input text. + * @property {string} suggestion Suggestion text of this component. + * @property {boolean} selected Whether this suggestion is currently selected. + * @property {Function} submit Submit function when the user clicks the suggestion. + */ + static get propTypes() { + return { + text: PropTypes.string, + suggestion: PropTypes.string, + selected: PropTypes.bool, + submit: PropTypes.func + }; + } + + onClick = () => { + this.props.submit(this.props.suggestion); + }; + + render() { + const { text, suggestion, selected } = this.props; + + let prefix = null; + let prefixLen = 0; + if (suggestion.substr(0, text.length) === text) { + prefix = ( + + {text} + + ); + prefixLen = text.length; + } + + const className = classnames({ + "sj-suggestion": true, + "sj-suggestion-selected": selected + }); + + return ( +
+ {prefix} + {suggestion.substr(prefixLen)} +
+ ); + } +} + +const getState = (values, pipeline, qParam, suggestionAmount) => { + const text = values.get()[qParam] || ""; + if (!text) { + return { text, suggestions: [], selectedPosition: -1 }; + } + const responseValues = pipeline.getResponse().getValues(); + const suggestions = responseValues + ? (responseValues["q.suggestions"] || "") + .split(",") + .filter((s, i) => s.length > 0 && i < suggestionAmount) + : []; + return { text, suggestions, selectedPosition: -1 }; +}; + +class AutocompleteDropdown extends React.Component { + /** + * propTypes + * @property {Values} values Values object. + * @property {Pipeline} pipeline Pipeline object. + * @property {string} placeholder Placeholder to use. + * @property {number} suggestionAmount Maximum number of suggestion to show. + * @property {Function} submit Submit function to call when a query has been chosen. + * @property {string} [qParam="q"] Search parameter. + * @property {boolean} [autoFocus=false] Whether to focus the input element. + */ + static get propTypes() { + return { + values: PropTypes.instanceOf(Values).isRequired, + pipeline: PropTypes.instanceOf(Pipeline).isRequired, + placeholder: PropTypes.string, + suggestionAmount: PropTypes.number, + submit: PropTypes.func, + qParam: PropTypes.string, + autoFocus: PropTypes.bool + }; + } + + constructor(props) { + super(props); + + this.state = getState( + props.values, + props.pipeline, + props.qParam, + props.suggestionAmount + ); + + this.removeValuesListener = this.props.values.listen( + valuesUpdatedEvent, + this.valuesChanged + ); + this.removeResponseListener = this.props.pipeline.listen( + responseUpdatedEvent, + this.valuesChanged + ); + } + + componentWillUnmount() { + this.removeValuesListener(); + this.removeResponseListener(); + } + + valuesChanged = () => { + const { values, pipeline, qParam, suggestionAmount } = this.props; + this.setState(getState(values, pipeline, qParam, suggestionAmount)); + }; + + setText = text => { + const { qParam, values, pipeline } = this.props; + const textValues = { [qParam]: text }; + values.set(textValues); + if (textValues[qParam]) { + pipeline.search(values.get()); + } else { + pipeline.clearResponse(values.get()); + } + }; + + submit = query => { + const { submit } = this.props; + submit(query); + this.setState({ text: query, suggestions: [], selectedPosition: -1 }); + }; + + handleChange = e => { + this.setText(e.target.value); + }; + + handleKeyDown = e => { + const { submit } = this.props; + const { text, suggestions, selectedPosition } = this.state; + if (e.keyCode === ESC_KEYCODE) { + this.setState({ suggestions: [], selectedPosition: -1 }); + return; + } + if (e.keyCode === RETURN_KEYCODE) { + if (selectedPosition >= 0) { + this.submit(suggestions[selectedPosition]); + } else { + this.submit(text); + } + return; + } + if (e.keyCode === UP_ARROW_KEYCODE) { + e.preventDefault(); + if (selectedPosition >= 0) { + this.setState({ + selectedPosition: selectedPosition - 1 + }); + } + return; + } + if (e.keyCode === DOWN_ARROW_KEYCODE) { + e.preventDefault(); + if (selectedPosition < suggestions.length - 1) { + this.setState({ + selectedPosition: selectedPosition + 1 + }); + } + return; + } + if ( + (e.keyCode === RIGHT_ARROW_KEYCODE || e.keyCode === TAB_KEYCODE) && + selectedPosition >= 0 + ) { + e.preventDefault(); + this.setText(suggestions[selectedPosition]); + this.setState({ selectedPosition: -1 }); + } + }; + + render() { + const { text, suggestions, selectedPosition } = this.state; + const { placeholder, autoFocus } = this.props; + + const inputText = + selectedPosition === -1 ? text : suggestions[selectedPosition]; + + return ( +
+ +
+ {suggestions.map((s, i) => + + {s} + + )} +
+
+ ); + } +} + +AutocompleteDropdown.defaultProps = { + qParam: "q", + suggestionAmount: 10, + placeHolder: "Search" +}; + +export default AutocompleteDropdown; From 8944392e17ab01f9d749139e274857aa4a56f80e Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Thu, 24 Aug 2017 11:21:17 +1000 Subject: [PATCH 02/61] ui: export AutocompleteDropdown from text index --- src/ui/text/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/text/index.js b/src/ui/text/index.js index 6f97bca19..a4f7a89a9 100644 --- a/src/ui/text/index.js +++ b/src/ui/text/index.js @@ -1,2 +1,3 @@ export { default as AutocompleteInput } from "./AutocompleteInput"; +export { default as AutocompleteDropdown } from "./AutocompleteDropdown"; export { default as Input } from "./Input"; From 3657f34da4139687134d95413e4a93b4e3dec6dd Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Thu, 24 Aug 2017 11:21:41 +1000 Subject: [PATCH 03/61] ui: remove autocapitalise property on autocompletedropdown, too opinionated --- src/ui/text/AutocompleteDropdown.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index 2e2384ad6..6e1ab788e 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -208,7 +208,6 @@ class AutocompleteDropdown extends React.Component {
Date: Thu, 24 Aug 2017 11:22:00 +1000 Subject: [PATCH 04/61] ui: show autocomplete suggestions using lowercase query text --- src/ui/text/AutocompleteDropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index 6e1ab788e..bdd29f344 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -220,7 +220,7 @@ class AutocompleteDropdown extends React.Component { From 2097c0d8220092270de49ee1222e0c6fdc33ccb9 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 10:18:53 +1000 Subject: [PATCH 05/61] ui: AutocompleteDropdown show completion, more options --- src/ui/text/AutocompleteDropdown.js | 113 +++++++++++++++++++++------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index bdd29f344..87f28adbc 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -69,15 +69,16 @@ class AutocompleteSuggestion extends React.Component { const getState = (values, pipeline, qParam, suggestionAmount) => { const text = values.get()[qParam] || ""; if (!text) { - return { text, suggestions: [], selectedPosition: -1 }; + return { text, completion: "", suggestions: [], selectedPosition: -1 }; } const responseValues = pipeline.getResponse().getValues(); + const completion = text && responseValues ? responseValues[qParam] || "" : ""; const suggestions = responseValues ? (responseValues["q.suggestions"] || "") .split(",") .filter((s, i) => s.length > 0 && i < suggestionAmount) : []; - return { text, suggestions, selectedPosition: -1 }; + return { text, completion, suggestions, selectedPosition: -1 }; }; class AutocompleteDropdown extends React.Component { @@ -87,9 +88,13 @@ class AutocompleteDropdown extends React.Component { * @property {Pipeline} pipeline Pipeline object. * @property {string} placeholder Placeholder to use. * @property {number} suggestionAmount Maximum number of suggestion to show. - * @property {Function} submit Submit function to call when a query has been chosen. + * @property {Function} handleSubmit Submit function to call when a query has been chosen. + * @property {Function} handleUpdate Update function to call when a query has been modified. * @property {string} [qParam="q"] Search parameter. + * @property {string} [qParam="q.override"] Search parameter. * @property {boolean} [autoFocus=false] Whether to focus the input element. + * @property {boolean} [search=true] Whether to search on text updated. + * @property {boolean} [showCompletion=true] Whether to show completions or not. */ static get propTypes() { return { @@ -97,9 +102,13 @@ class AutocompleteDropdown extends React.Component { pipeline: PropTypes.instanceOf(Pipeline).isRequired, placeholder: PropTypes.string, suggestionAmount: PropTypes.number, - submit: PropTypes.func, + handleSubmit: PropTypes.func, + handleUpdate: PropTypes.func, qParam: PropTypes.string, - autoFocus: PropTypes.bool + qOverrideParam: PropTypes.string, + autoFocus: PropTypes.bool, + search: PropTypes.bool, + showCompletion: PropTypes.bool }; } @@ -123,6 +132,25 @@ class AutocompleteDropdown extends React.Component { ); } + componentWillUpdate(nextProps) { + const { values: newValues, pipeline: newPipeline } = nextProps; + const { values: oldValues, pipeline: oldPipeline } = this.props; + if (newValues !== oldValues) { + this.removeValuesListener(); + this.removeValuesListener = newValues.listen( + valuesUpdatedEvent, + this.valuesChanged + ); + } + if (newPipeline !== oldPipeline) { + this.removeResponseListener(); + this.removeResponseListener = newPipeline.listen( + responseUpdatedEvent, + this.valuesChanged + ); + } + } + componentWillUnmount() { this.removeValuesListener(); this.removeResponseListener(); @@ -134,9 +162,12 @@ class AutocompleteDropdown extends React.Component { }; setText = text => { - const { qParam, values, pipeline } = this.props; - const textValues = { [qParam]: text }; + const { qParam, qOverrideParam, values, pipeline, search } = this.props; + const textValues = { [qParam]: text, [qOverrideParam]: undefined }; values.set(textValues); + if (!search) { + return; + } if (textValues[qParam]) { pipeline.search(values.get()); } else { @@ -145,19 +176,22 @@ class AutocompleteDropdown extends React.Component { }; submit = query => { - const { submit } = this.props; - submit(query); + this.props.handleSubmit(query); this.setState({ text: query, suggestions: [], selectedPosition: -1 }); }; handleChange = e => { this.setText(e.target.value); + this.props.handleUpdate(e.target.value); }; handleKeyDown = e => { - const { submit } = this.props; - const { text, suggestions, selectedPosition } = this.state; + const { handleUpdate } = this.props; + const { text, completion, suggestions, selectedPosition } = this.state; if (e.keyCode === ESC_KEYCODE) { + if (selectedPosition !== -1) { + handleUpdate(text); + } this.setState({ suggestions: [], selectedPosition: -1 }); return; } @@ -187,34 +221,50 @@ class AutocompleteDropdown extends React.Component { } return; } - if ( - (e.keyCode === RIGHT_ARROW_KEYCODE || e.keyCode === TAB_KEYCODE) && - selectedPosition >= 0 - ) { + if (e.keyCode === RIGHT_ARROW_KEYCODE || e.keyCode === TAB_KEYCODE) { e.preventDefault(); - this.setText(suggestions[selectedPosition]); - this.setState({ selectedPosition: -1 }); + if (selectedPosition >= 0) { + this.setText(suggestions[selectedPosition]); + this.setState({ selectedPosition: -1 }); + } else if (completion) { + this.setState({ text: completion, suggestions: [] }); + } } }; render() { - const { text, suggestions, selectedPosition } = this.state; - const { placeholder, autoFocus } = this.props; + const { text, completion, suggestions, selectedPosition } = this.state; + const { placeholder, autoFocus, showCompletion } = this.props; const inputText = selectedPosition === -1 ? text : suggestions[selectedPosition]; return (
- +
+
+ + + +
+
{suggestions.map((s, i) => {}, + handleUpdate: () => {}, + search: true, + showCompletion: true }; export default AutocompleteDropdown; From 942e89fdbbd98deed86cf28109971cfab24e3ab8 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 10:19:28 +1000 Subject: [PATCH 06/61] ui: AutocompleteDropdown remove unneeded styles --- src/ui/text/AutocompleteDropdown.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ui/text/AutocompleteDropdown.css b/src/ui/text/AutocompleteDropdown.css index 1550b4940..8c9101862 100644 --- a/src/ui/text/AutocompleteDropdown.css +++ b/src/ui/text/AutocompleteDropdown.css @@ -1,5 +1,4 @@ .sj-suggestions { - position: absolute; width: 100%; } @@ -7,11 +6,6 @@ position: relative; } -.sj-search-bar-input { - position: static; - background-color: white; -} - .sj-suggestion { font-size: 18px; padding: 8px 8px; From ba96a9a1d2f8c8c89e61f68bfa70f2ec197153fa Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 15:46:27 +1000 Subject: [PATCH 07/61] ui: restructure input component html layout and css --- src/ui/overlay/Overlay.css | 13 +++-- src/ui/text/AutocompleteDropdown.css | 18 ++----- src/ui/text/AutocompleteDropdown.js | 73 ++++++++++++++-------------- src/ui/text/AutocompleteInput.css | 33 ++++++++----- 4 files changed, 67 insertions(+), 70 deletions(-) diff --git a/src/ui/overlay/Overlay.css b/src/ui/overlay/Overlay.css index 99726e625..2b467e6bd 100644 --- a/src/ui/overlay/Overlay.css +++ b/src/ui/overlay/Overlay.css @@ -29,8 +29,7 @@ } .sj-overlay-search { - padding-left: 10px; - padding-right: 10px; + padding: 0px 30px; } .sj-overlay-close { @@ -57,12 +56,12 @@ font-size: 12px; } -@media (max-width: 845px) { - .sj-overlay-close { - right: 3%; +@media (max-width: 768px) { + .sj-overlay-search { + padding: 0px 10px; } - .sj-overlay .sj-search-bar-input-common { - width: 70vw; + .sj-overlay .sj-search-holder-outer { + margin-right: 54px; } } diff --git a/src/ui/text/AutocompleteDropdown.css b/src/ui/text/AutocompleteDropdown.css index 8c9101862..f9084067f 100644 --- a/src/ui/text/AutocompleteDropdown.css +++ b/src/ui/text/AutocompleteDropdown.css @@ -1,28 +1,18 @@ -.sj-suggestions { - width: 100%; -} - .sj-autocomplete-dropdown { position: relative; } +.sj-suggestions { + border: 1px solid #ddd; +} + .sj-suggestion { font-size: 18px; padding: 8px 8px; - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; background-color: #fff; color: #666; } -.sj-suggestion:first-child { - border-top: 1px solid #ddd; -} - -.sj-suggestion:last-child { - border-bottom: 1px solid #ddd; -} - .sj-suggestion strong { font-weight: 600; color: #333; diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index 87f28adbc..bd9d443e7 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -240,43 +240,42 @@ class AutocompleteDropdown extends React.Component { selectedPosition === -1 ? text : suggestions[selectedPosition]; return ( -
-
-
- - - -
-
-
- {suggestions.map((s, i) => - - {s} - - )} +
+
+ + + {suggestions.length > 0 + ?
+ {suggestions.map((s, i) => + + {s} + + )} +
+ : null}
); diff --git a/src/ui/text/AutocompleteInput.css b/src/ui/text/AutocompleteInput.css index a8042f084..d21ec30dd 100644 --- a/src/ui/text/AutocompleteInput.css +++ b/src/ui/text/AutocompleteInput.css @@ -2,38 +2,41 @@ * Search box */ -.sj-search-input-holder-outer { +.sj-search-holder-outer { padding: 0.9em 0px; position: relative; - height: 28px; + min-height: 72px; + box-sizing: border-box; } -.sj-search-input-holder-inner { +.sj-overlay .sj-search-holder-outer { + margin-right: 34px; +} + +.sj-search-holder-inner { position: absolute; + width: 100%; } .sj-search-bar-input-common { - width: 500px; font-size: 20px; padding: 0.4em; outline: none; letter-spacing: 0.6px; - margin-top: auto; - margin-bottom: auto; - margin-left: auto; - line-height: 24px; + line-height: 28px; text-rendering: optimizeLegibility; - border-radius: 0; - box-sizing: initial; + width: 100%; + box-shadow: 0 0 0 1px #ddd; + border-radius: 3px; + border: 0; + box-sizing: inherit; } .sj-search-bar-completion { - border: 1px solid #d9d9d9; color: #bebebe; } .sj-search-bar-input { - border: 1px solid transparent; position: absolute; background: transparent; color: #666; @@ -58,3 +61,9 @@ .sj-result-summary-autocomplete-override > a { color: #1a0dab; } + +@media (max-width: 768px) { + .sj-overlay .sj-search-holder-outer { + margin-right: 54px; + } +} From 2739c4c1e3bc46e601bfb0edfd1be6ea7fa582b3 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 15:47:50 +1000 Subject: [PATCH 08/61] example: add control options to autocomplete search example --- examples/autocomplete-search/src/App.js | 92 ++++++++++++++++++++----- 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-search/src/App.js index d6b159c69..e8a631155 100644 --- a/examples/autocomplete-search/src/App.js +++ b/examples/autocomplete-search/src/App.js @@ -16,22 +16,82 @@ const websitePipeline = new Pipeline(project, collection, "website"); const autocompleteValues = new Values(); const websiteValues = new Values(); -const App = () => -
- { - websiteValues.set({ q: query, "q.override": true }); +class App extends React.Component { + constructor(props) { + super(props); + this.state = { autocomplete: true, instant: false }; + } + + update = query => { + if (this.state.instant && !this.state.autocomplete) { + websiteValues.set({ q: query, "q.override": undefined }); + if (query) { websitePipeline.search(websiteValues.get()); - }} - /> - - - - - -
; + return; + } + websitePipeline.clearResponse(websiteValues.get()); + return; + } + }; + + submit = query => { + if (query) { + websiteValues.set({ q: query, "q.override": true }); + websitePipeline.search(websiteValues.get()); + return; + } + websitePipeline.clearResponse(); + }; + + render() { + const { autocomplete, instant } = this.state; + + const valuesForAutocomplete = instant ? websiteValues : autocompleteValues; + const pipelineForAutocomplete = instant + ? websitePipeline + : autocompletePipeline; + + const suggestionAmount = autocomplete ? 5 : 0; + const searchAutocomplete = + (autocomplete || instant) && !(instant && !autocomplete); + const showCompletion = instant; + + return ( +
+ +
+ + + + + + + +
+ ); + } +} export default App; From 3e47c3da88c16c025851283ee07a7a99b0cc4e9f Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 15:53:05 +1000 Subject: [PATCH 09/61] sdk: bump to version 1.4.0 --- examples/autocomplete-search/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/autocomplete-search/package.json b/examples/autocomplete-search/package.json index efca67330..8673601e4 100644 --- a/examples/autocomplete-search/package.json +++ b/examples/autocomplete-search/package.json @@ -5,7 +5,7 @@ "dependencies": { "react": "15.6.1", "react-dom": "15.6.1", - "sajari-react": "1.3.4" + "sajari-react": "1.4.0" }, "devDependencies": { "react-scripts": "1.0.11" diff --git a/package.json b/package.json index 75e33a58c..07615a81e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sajari-react", "description": "React SDK for the Sajari API", - "version": "1.3.6", + "version": "1.4.0", "author": [ { "name": "Trent Billington", From 32b11fad4a0e4dece4de0bde7dfcc994876da91b Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 18:09:11 +1000 Subject: [PATCH 10/61] ui: rename props and reword descriptions on AutocompleteDropdown --- src/ui/text/AutocompleteDropdown.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index bd9d443e7..4733e0916 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -88,13 +88,13 @@ class AutocompleteDropdown extends React.Component { * @property {Pipeline} pipeline Pipeline object. * @property {string} placeholder Placeholder to use. * @property {number} suggestionAmount Maximum number of suggestion to show. - * @property {Function} handleSubmit Submit function to call when a query has been chosen. - * @property {Function} handleUpdate Update function to call when a query has been modified. + * @property {Function} handleForceSearch Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. + * @property {Function} handleUpdate Callback function called when the query has been modified. * @property {string} [qParam="q"] Search parameter. - * @property {string} [qParam="q.override"] Search parameter. + * @property {string} [qParam="q.override"] Search override parameter. * @property {boolean} [autoFocus=false] Whether to focus the input element. - * @property {boolean} [search=true] Whether to search on text updated. - * @property {boolean} [showCompletion=true] Whether to show completions or not. + * @property {boolean} [instant=true] Whether to search on text updated. + * @property {boolean} [showCompletion=true] Whether to show completions inline with the query text. */ static get propTypes() { return { @@ -102,12 +102,12 @@ class AutocompleteDropdown extends React.Component { pipeline: PropTypes.instanceOf(Pipeline).isRequired, placeholder: PropTypes.string, suggestionAmount: PropTypes.number, - handleSubmit: PropTypes.func, - handleUpdate: PropTypes.func, + handleForceSearch: PropTypes.func, + handleForceSearch: PropTypes.func, qParam: PropTypes.string, qOverrideParam: PropTypes.string, autoFocus: PropTypes.bool, - search: PropTypes.bool, + instant: PropTypes.bool, showCompletion: PropTypes.bool }; } @@ -162,10 +162,10 @@ class AutocompleteDropdown extends React.Component { }; setText = text => { - const { qParam, qOverrideParam, values, pipeline, search } = this.props; + const { qParam, qOverrideParam, values, pipeline, instant } = this.props; const textValues = { [qParam]: text, [qOverrideParam]: undefined }; values.set(textValues); - if (!search) { + if (!instant) { return; } if (textValues[qParam]) { @@ -176,7 +176,7 @@ class AutocompleteDropdown extends React.Component { }; submit = query => { - this.props.handleSubmit(query); + this.props.handleForceSearch(query); this.setState({ text: query, suggestions: [], selectedPosition: -1 }); }; @@ -287,9 +287,9 @@ AutocompleteDropdown.defaultProps = { qOverrideParam: "q.override", suggestionAmount: 10, placeHolder: "Search", - handleSubmit: () => {}, + handleForceSearch: () => {}, handleUpdate: () => {}, - search: true, + instant: true, showCompletion: true }; From 8062ea8efc054968f62ab67e9854705224f509c9 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 18:18:04 +1000 Subject: [PATCH 11/61] ui: fix incorrect proptype --- src/ui/text/AutocompleteDropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index 4733e0916..dcca9110f 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -91,7 +91,7 @@ class AutocompleteDropdown extends React.Component { * @property {Function} handleForceSearch Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. * @property {Function} handleUpdate Callback function called when the query has been modified. * @property {string} [qParam="q"] Search parameter. - * @property {string} [qParam="q.override"] Search override parameter. + * @property {string} [qOverrideParam="q.override"] Search override parameter. * @property {boolean} [autoFocus=false] Whether to focus the input element. * @property {boolean} [instant=true] Whether to search on text updated. * @property {boolean} [showCompletion=true] Whether to show completions inline with the query text. From f0523ed4be5e546d8666f674e76f18b91ae67b13 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 18:21:36 +1000 Subject: [PATCH 12/61] ui: AutocompleteDropdown reduce default suggestion amount --- src/ui/text/AutocompleteDropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index dcca9110f..b94fa1bc3 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -285,7 +285,7 @@ class AutocompleteDropdown extends React.Component { AutocompleteDropdown.defaultProps = { qParam: "q", qOverrideParam: "q.override", - suggestionAmount: 10, + suggestionAmount: 5, placeHolder: "Search", handleForceSearch: () => {}, handleUpdate: () => {}, From ca1b92b8434307e33fe1f8bacd118b1dab384764 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 18:25:06 +1000 Subject: [PATCH 13/61] ui: add readme section on AutocompleteDropdown --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 7e6f25a6b..ad39930c1 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,30 @@ import { Input } from "sajari-react/ui/text"; ``` +## AutocompleteDropdown + +`AutocompleteDropdown` is a text box which performs autocomplete as the user types and renders autocomplete suggestions in a list underneath. + +| Prop | Type | Description | +| :-- | :-: | :-- | +| values | `Values` | Values object. | +| pipeline | `Pipeline` | Pipeline object. | +| placeholder | `string` | Placeholder to use. | +| suggestionAmount | `number` | Maximum number of suggestion to show. | +| handleForceSearch | `Function` | Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. | +| handleUpdate | `Function` | Callback function called when the query has been modified. | +| qParam | `string` | Search parameter. | +| qParam | `string` | Search override parameter. | +| autoFocus | `boolean` | Whether to focus the input element. | +| instant | `boolean` | Whether to search on text updated. | +| showCompletion | `boolean` | Whether to show completions inline with the query text. | + +```javascript +import { AutocompleteDropdown } from "sajari-react/ui/text"; + + +``` + ## Handling results A typical search result UI could includes a summary of the search, maybe with options to change spellings or search for alternatives, and a list of the paginated results. We include components for all of these pieces so it's easy to get started. From fe8e1535d6e5bd7199087123a2106aa0590bb608 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Fri, 25 Aug 2017 18:26:20 +1000 Subject: [PATCH 14/61] examples: update autocomplete search to match new AutocompleteDropdown prop naming --- examples/autocomplete-search/src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-search/src/App.js index e8a631155..ac840304d 100644 --- a/examples/autocomplete-search/src/App.js +++ b/examples/autocomplete-search/src/App.js @@ -80,7 +80,7 @@ class App extends React.Component { pipeline={pipelineForAutocomplete} suggestionAmount={suggestionAmount} handleUpdate={this.update} - handleSubmit={this.submit} + handleForceSearch={this.submit} search={searchAutocomplete} showCompletion={showCompletion} /> From 23a4bab8e8c2b45bc3ac2c0a0152c443217c9b0a Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 15:07:31 +1000 Subject: [PATCH 15/61] ui: rename css classes in AutocompleteInput to match new names --- src/ui/text/AutocompleteInput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/text/AutocompleteInput.js b/src/ui/text/AutocompleteInput.js index 09dc6abb1..8feaada96 100644 --- a/src/ui/text/AutocompleteInput.js +++ b/src/ui/text/AutocompleteInput.js @@ -109,8 +109,8 @@ class AutocompleteInput extends React.Component { const { placeholder, autoFocus } = this.props; return ( -
-
+
+
Date: Mon, 28 Aug 2017 15:44:12 +1000 Subject: [PATCH 16/61] examples: update to latest sajari-react version 1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 07615a81e..75e33a58c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sajari-react", "description": "React SDK for the Sajari API", - "version": "1.4.0", + "version": "1.3.6", "author": [ { "name": "Trent Billington", From b5ee1afa7c89124c69766e0591a69049d862c351 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 15:44:35 +1000 Subject: [PATCH 17/61] examples: rename autocomplete-search to autocomplete-dropdown --- examples/autocomplete-search/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/autocomplete-search/package.json b/examples/autocomplete-search/package.json index 8673601e4..b048218a4 100644 --- a/examples/autocomplete-search/package.json +++ b/examples/autocomplete-search/package.json @@ -1,5 +1,5 @@ { - "name": "autocomplete-search", + "name": "autocomplete-dropdown", "version": "0.1.0", "private": true, "dependencies": { From ebaa818c84e43f31bc940afb5688741f0f787c7f Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 15:46:24 +1000 Subject: [PATCH 18/61] ui: improve AutocompleteDropdown prop names --- src/ui/text/AutocompleteDropdown.js | 68 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/ui/text/AutocompleteDropdown.js b/src/ui/text/AutocompleteDropdown.js index b94fa1bc3..4c82b238d 100644 --- a/src/ui/text/AutocompleteDropdown.js +++ b/src/ui/text/AutocompleteDropdown.js @@ -66,7 +66,7 @@ class AutocompleteSuggestion extends React.Component { } } -const getState = (values, pipeline, qParam, suggestionAmount) => { +const getState = (values, pipeline, qParam, numSuggestions) => { const text = values.get()[qParam] || ""; if (!text) { return { text, completion: "", suggestions: [], selectedPosition: -1 }; @@ -76,7 +76,7 @@ const getState = (values, pipeline, qParam, suggestionAmount) => { const suggestions = responseValues ? (responseValues["q.suggestions"] || "") .split(",") - .filter((s, i) => s.length > 0 && i < suggestionAmount) + .filter((s, i) => s.length > 0 && i < numSuggestions) : []; return { text, completion, suggestions, selectedPosition: -1 }; }; @@ -86,29 +86,29 @@ class AutocompleteDropdown extends React.Component { * propTypes * @property {Values} values Values object. * @property {Pipeline} pipeline Pipeline object. - * @property {string} placeholder Placeholder to use. - * @property {number} suggestionAmount Maximum number of suggestion to show. + * @property {string} placeholder Placeholder to use for the input element. + * @property {number} [numSuggestions=5] Maximum number of suggestion to show. * @property {Function} handleForceSearch Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. - * @property {Function} handleUpdate Callback function called when the query has been modified. + * @property {Function} handleQueryChanged Callback function called when the query has been modified. * @property {string} [qParam="q"] Search parameter. * @property {string} [qOverrideParam="q.override"] Search override parameter. - * @property {boolean} [autoFocus=false] Whether to focus the input element. - * @property {boolean} [instant=true] Whether to search on text updated. - * @property {boolean} [showCompletion=true] Whether to show completions inline with the query text. + * @property {boolean} [autoFocus=false] Whether to focus the input element on creation. + * @property {boolean} [autocompleteOnQueryChanged=true] Whether to search autocomplete on query change. + * @property {boolean} [showInlineCompletion=true] Whether to show completions inline with the query text. */ static get propTypes() { return { values: PropTypes.instanceOf(Values).isRequired, pipeline: PropTypes.instanceOf(Pipeline).isRequired, placeholder: PropTypes.string, - suggestionAmount: PropTypes.number, - handleForceSearch: PropTypes.func, + numSuggestions: PropTypes.number, handleForceSearch: PropTypes.func, + handleQueryChanged: PropTypes.func, qParam: PropTypes.string, qOverrideParam: PropTypes.string, autoFocus: PropTypes.bool, - instant: PropTypes.bool, - showCompletion: PropTypes.bool + autocompleteOnQueryChanged: PropTypes.bool, + showInlineCompletion: PropTypes.bool }; } @@ -119,7 +119,7 @@ class AutocompleteDropdown extends React.Component { props.values, props.pipeline, props.qParam, - props.suggestionAmount + props.numSuggestions ); this.removeValuesListener = this.props.values.listen( @@ -157,15 +157,21 @@ class AutocompleteDropdown extends React.Component { } valuesChanged = () => { - const { values, pipeline, qParam, suggestionAmount } = this.props; - this.setState(getState(values, pipeline, qParam, suggestionAmount)); + const { values, pipeline, qParam, numSuggestions } = this.props; + this.setState(getState(values, pipeline, qParam, numSuggestions)); }; setText = text => { - const { qParam, qOverrideParam, values, pipeline, instant } = this.props; + const { + qParam, + qOverrideParam, + values, + pipeline, + autocompleteOnQueryChanged + } = this.props; const textValues = { [qParam]: text, [qOverrideParam]: undefined }; values.set(textValues); - if (!instant) { + if (!autocompleteOnQueryChanged) { return; } if (textValues[qParam]) { @@ -182,15 +188,15 @@ class AutocompleteDropdown extends React.Component { handleChange = e => { this.setText(e.target.value); - this.props.handleUpdate(e.target.value); + this.props.handleQueryChanged(e.target.value); }; handleKeyDown = e => { - const { handleUpdate } = this.props; + const { handleQueryChanged } = this.props; const { text, completion, suggestions, selectedPosition } = this.state; if (e.keyCode === ESC_KEYCODE) { if (selectedPosition !== -1) { - handleUpdate(text); + handleQueryChanged(text); } this.setState({ suggestions: [], selectedPosition: -1 }); return; @@ -234,10 +240,11 @@ class AutocompleteDropdown extends React.Component { render() { const { text, completion, suggestions, selectedPosition } = this.state; - const { placeholder, autoFocus, showCompletion } = this.props; + const { placeholder, autoFocus, showInlineCompletion } = this.props; - const inputText = - selectedPosition === -1 ? text : suggestions[selectedPosition]; + const completionValue = showInlineCompletion + ? completion.indexOf(text) === 0 ? completion : text + : ""; return (
@@ -245,11 +252,7 @@ class AutocompleteDropdown extends React.Component { {}, - handleUpdate: () => {}, - instant: true, - showCompletion: true + handleQueryChanged: () => {}, + autocompleteOnQueryChanged: true, + showInlineCompletion: true }; export default AutocompleteDropdown; From d8389a0af03035d58c08b509676e6dc9ed2b93e1 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 15:47:09 +1000 Subject: [PATCH 19/61] examples: autocomplete-search simplify logic, use new prop names for AutocompleteDropdown --- examples/autocomplete-search/src/App.js | 55 ++++++------------------- 1 file changed, 12 insertions(+), 43 deletions(-) diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-search/src/App.js index ac840304d..5469b4ef4 100644 --- a/examples/autocomplete-search/src/App.js +++ b/examples/autocomplete-search/src/App.js @@ -11,29 +11,15 @@ import "sajari-react/ui/results/Paginator.css"; const project = "sajariptyltd"; const collection = "sajari-com"; -const autocompletePipeline = new Pipeline(project, collection, "autocomplete"); const websitePipeline = new Pipeline(project, collection, "website"); -const autocompleteValues = new Values(); const websiteValues = new Values(); class App extends React.Component { constructor(props) { super(props); - this.state = { autocomplete: true, instant: false }; + this.state = { autocomplete: true }; } - update = query => { - if (this.state.instant && !this.state.autocomplete) { - websiteValues.set({ q: query, "q.override": undefined }); - if (query) { - websitePipeline.search(websiteValues.get()); - return; - } - websitePipeline.clearResponse(websiteValues.get()); - return; - } - }; - submit = query => { if (query) { websiteValues.set({ q: query, "q.override": true }); @@ -43,19 +29,12 @@ class App extends React.Component { websitePipeline.clearResponse(); }; - render() { - const { autocomplete, instant } = this.state; - - const valuesForAutocomplete = instant ? websiteValues : autocompleteValues; - const pipelineForAutocomplete = instant - ? websitePipeline - : autocompletePipeline; - - const suggestionAmount = autocomplete ? 5 : 0; - const searchAutocomplete = - (autocomplete || instant) && !(instant && !autocomplete); - const showCompletion = instant; + toggleAutocomplete = () => { + this.setState({ autocomplete: !this.state.autocomplete }); + }; + render() { + const { autocomplete } = this.state; return (
-
- From 3959452a19509fe59952152c5b93cf0b73a36907 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 15:47:55 +1000 Subject: [PATCH 20/61] readme: update AutocompleteDropdown readme entry to match code --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ad39930c1..1b5beef91 100644 --- a/README.md +++ b/README.md @@ -94,14 +94,14 @@ import { Input } from "sajari-react/ui/text"; | values | `Values` | Values object. | | pipeline | `Pipeline` | Pipeline object. | | placeholder | `string` | Placeholder to use. | -| suggestionAmount | `number` | Maximum number of suggestion to show. | +| numSuggestions | `number` | Maximum number of suggestion to show. | | handleForceSearch | `Function` | Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. | -| handleUpdate | `Function` | Callback function called when the query has been modified. | +| handleQueryChanged | `Function` | Callback function called when the query has been modified. | | qParam | `string` | Search parameter. | -| qParam | `string` | Search override parameter. | +| qOverrideParam | `string` | Search override parameter. | | autoFocus | `boolean` | Whether to focus the input element. | -| instant | `boolean` | Whether to search on text updated. | -| showCompletion | `boolean` | Whether to show completions inline with the query text. | +| autocompleteOnQueryChanged | `boolean` | Whether to search autocomplete on query change. | +| showInlineCompletion | `boolean` | Whether to show completions inline with the query text. | ```javascript import { AutocompleteDropdown } from "sajari-react/ui/text"; From 0587716d557da7986a118ccc7e52351988023011 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Mon, 28 Aug 2017 16:38:59 +1000 Subject: [PATCH 21/61] ui: fix css sizing issue with AutocompleteInput --- src/ui/text/AutocompleteInput.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/text/AutocompleteInput.css b/src/ui/text/AutocompleteInput.css index d21ec30dd..aea9252ef 100644 --- a/src/ui/text/AutocompleteInput.css +++ b/src/ui/text/AutocompleteInput.css @@ -29,7 +29,7 @@ box-shadow: 0 0 0 1px #ddd; border-radius: 3px; border: 0; - box-sizing: inherit; + box-sizing: border-box; } .sj-search-bar-completion { From c01b90ed812245ef621290bb9752b91b59580497 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Tue, 29 Aug 2017 15:06:40 +1000 Subject: [PATCH 22/61] readme: AutocompleteDropdown remove table, add picture --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index 1b5beef91..574707452 100644 --- a/README.md +++ b/README.md @@ -89,19 +89,7 @@ import { Input } from "sajari-react/ui/text"; `AutocompleteDropdown` is a text box which performs autocomplete as the user types and renders autocomplete suggestions in a list underneath. -| Prop | Type | Description | -| :-- | :-: | :-- | -| values | `Values` | Values object. | -| pipeline | `Pipeline` | Pipeline object. | -| placeholder | `string` | Placeholder to use. | -| numSuggestions | `number` | Maximum number of suggestion to show. | -| handleForceSearch | `Function` | Callback function called when a user presses Enter while highlighting a suggestion or clicks a suggestion. | -| handleQueryChanged | `Function` | Callback function called when the query has been modified. | -| qParam | `string` | Search parameter. | -| qOverrideParam | `string` | Search override parameter. | -| autoFocus | `boolean` | Whether to focus the input element. | -| autocompleteOnQueryChanged | `boolean` | Whether to search autocomplete on query change. | -| showInlineCompletion | `boolean` | Whether to show completions inline with the query text. | +![autocomplete-suggest](https://media.giphy.com/media/xT39DnBWdmrxJ1Xd1S/giphy.gif) ```javascript import { AutocompleteDropdown } from "sajari-react/ui/text"; From 08b62408946c303f362ff8a9244e9fd94b28ca90 Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Tue, 29 Aug 2017 15:07:25 +1000 Subject: [PATCH 23/61] examples: autocomplete-dropdown make example more simple --- examples/autocomplete-search/src/App.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-search/src/App.js index 5469b4ef4..2acd4ac18 100644 --- a/examples/autocomplete-search/src/App.js +++ b/examples/autocomplete-search/src/App.js @@ -15,11 +15,6 @@ const websitePipeline = new Pipeline(project, collection, "website"); const websiteValues = new Values(); class App extends React.Component { - constructor(props) { - super(props); - this.state = { autocomplete: true }; - } - submit = query => { if (query) { websiteValues.set({ q: query, "q.override": true }); @@ -29,26 +24,13 @@ class App extends React.Component { websitePipeline.clearResponse(); }; - toggleAutocomplete = () => { - this.setState({ autocomplete: !this.state.autocomplete }); - }; - render() { - const { autocomplete } = this.state; return (
- Date: Tue, 29 Aug 2017 15:08:56 +1000 Subject: [PATCH 24/61] examples: rename autocomplete-search to autocomplete-dropdown --- .../package.json | 0 .../public/favicon.ico | Bin .../public/index.html | 0 .../src/App.js | 0 .../src/index.js | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename examples/{autocomplete-search => autocomplete-dropdown}/package.json (100%) rename examples/{autocomplete-search => autocomplete-dropdown}/public/favicon.ico (100%) rename examples/{autocomplete-search => autocomplete-dropdown}/public/index.html (100%) rename examples/{autocomplete-search => autocomplete-dropdown}/src/App.js (100%) rename examples/{autocomplete-search => autocomplete-dropdown}/src/index.js (100%) diff --git a/examples/autocomplete-search/package.json b/examples/autocomplete-dropdown/package.json similarity index 100% rename from examples/autocomplete-search/package.json rename to examples/autocomplete-dropdown/package.json diff --git a/examples/autocomplete-search/public/favicon.ico b/examples/autocomplete-dropdown/public/favicon.ico similarity index 100% rename from examples/autocomplete-search/public/favicon.ico rename to examples/autocomplete-dropdown/public/favicon.ico diff --git a/examples/autocomplete-search/public/index.html b/examples/autocomplete-dropdown/public/index.html similarity index 100% rename from examples/autocomplete-search/public/index.html rename to examples/autocomplete-dropdown/public/index.html diff --git a/examples/autocomplete-search/src/App.js b/examples/autocomplete-dropdown/src/App.js similarity index 100% rename from examples/autocomplete-search/src/App.js rename to examples/autocomplete-dropdown/src/App.js diff --git a/examples/autocomplete-search/src/index.js b/examples/autocomplete-dropdown/src/index.js similarity index 100% rename from examples/autocomplete-search/src/index.js rename to examples/autocomplete-dropdown/src/index.js From a069e41c683c69101df5ec168b8a93025e05b10b Mon Sep 17 00:00:00 2001 From: Trent Billington Date: Tue, 29 Aug 2017 16:22:45 +1000 Subject: [PATCH 25/61] example: better example page title --- examples/autocomplete-dropdown/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/autocomplete-dropdown/public/index.html b/examples/autocomplete-dropdown/public/index.html index 8a8b77e93..7745985ab 100644 --- a/examples/autocomplete-dropdown/public/index.html +++ b/examples/autocomplete-dropdown/public/index.html @@ -4,7 +4,7 @@ - Example: Autocomplete Search + Example: Autocomplete Dropdown