optimizely-oui
Version:
Optimizely's Component Library.
383 lines (314 loc) • 15.3 kB
JavaScript
var _class, _class2, _temp;
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import { debounce, noop } from "lodash";
import PropTypes from "prop-types";
import React from "react";
import { keyboardTracker } from "../../utils/accessibility";
import Input from "../Input";
var INITIAL_SEARCH_PAGE_SIZE = 5;
var INPUT_DEBOUNCE_PERIOD = 120;
var SearchPicker = keyboardTracker(_class = (_temp = _class2 =
/*#__PURE__*/
function (_React$Component) {
_inherits(SearchPicker, _React$Component);
function SearchPicker() {
var _getPrototypeOf2;
var _this;
_classCallCheck(this, SearchPicker);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(SearchPicker)).call.apply(_getPrototypeOf2, [this].concat(args)));
_defineProperty(_assertThisInitialized(_this), "componentDidMount", function () {
var _this$props = _this.props,
searchFunction = _this$props.searchFunction,
searchOptions = _this$props.searchOptions,
setOnItemSelect = _this$props.setOnItemSelect,
supportedTypes = _this$props.supportedTypes;
setOnItemSelect(function () {
return _this.handleItemSelected;
});
searchFunction(_objectSpread({}, searchOptions, {
types: supportedTypes,
query: "",
per_page: INITIAL_SEARCH_PAGE_SIZE
})).then(function (results) {
// Make this Promise catch if the results are not an array
_this.verifyResultOrThrow(results);
_this.setState({
defaultResults: results
});
})["finally"](function () {
var searchQuery = _this.state.searchQuery; // Always set loading false as long as searchQuery is empty
if (!searchQuery) {
_this.setState({
isLoading: false
});
}
});
});
_defineProperty(_assertThisInitialized(_this), "componentDidUpdate", function () {
var items = _this.getAvailableEntities();
_this.props.setItemCount(items.length);
});
_defineProperty(_assertThisInitialized(_this), "state", {
currentSearch: null,
defaultResults: [],
isLoading: true,
results: null,
searchQuery: ""
});
_defineProperty(_assertThisInitialized(_this), "handleItemSelected", function (index) {
var onItemSelected = _this.props.onItemSelected;
var searchQuery = _this.state.searchQuery;
onItemSelected(index, _this.getAvailableEntities(), searchQuery);
});
_defineProperty(_assertThisInitialized(_this), "handleOnInput", function (event) {
var value = event.target.value;
_this.setState({
searchQuery: value
});
if (value) {
_this.handleOnInputDebounced(value);
} else {
var currentSearch = _this.state.currentSearch;
if (currentSearch) {
currentSearch.reject("Search interrupted by empty search query.");
}
_this.setState({
isLoading: false,
results: null,
currentSearch: null
});
}
});
_defineProperty(_assertThisInitialized(_this), "executeNewSearch", function (query) {
return new Promise(function (resolve, reject) {
var _this$props2 = _this.props,
searchFunction = _this$props2.searchFunction,
searchOptions = _this$props2.searchOptions,
supportedTypes = _this$props2.supportedTypes;
var currentSearch = _this.state.currentSearch;
if (currentSearch) {
currentSearch.reject("Search interrupted by new search query: ".concat(query));
}
_this.setState({
currentSearch: {
resolve: resolve,
reject: reject
},
isLoading: true
});
searchFunction(_objectSpread({}, searchOptions, {
types: supportedTypes,
query: query.toLowerCase()
})).then(resolve, reject);
});
});
_defineProperty(_assertThisInitialized(_this), "handleOnInputDebounced", debounce(function (query) {
_this.executeNewSearch(query).then(function (results) {
var searchQuery = _this.state.searchQuery;
_this.setState({
isLoading: false,
currentSearch: null
}); // Ensure the query wasn't killed while waiting for the result.
if (searchQuery) {
_this.setState({
results: results
});
}
}, function (e) {
/** Swallow rejections */
});
}, !!process.env.JEST_WORKER_ID ? 0 : INPUT_DEBOUNCE_PERIOD));
_defineProperty(_assertThisInitialized(_this), "getAvailableEntities", function () {
var _this$props3 = _this.props,
selectedEntityIds = _this$props3.selectedEntityIds,
filterAvailableEntities = _this$props3.filterAvailableEntities;
if (typeof filterAvailableEntities === "function") {
return filterAvailableEntities(_this.getResultSet(), selectedEntityIds);
}
return _this.getResultSet().filter(function (result) {
return !selectedEntityIds.includes(result.id);
});
});
_defineProperty(_assertThisInitialized(_this), "getResultSet", function () {
var _this$state = _this.state,
results = _this$state.results,
defaultResults = _this$state.defaultResults;
return results || defaultResults;
});
_defineProperty(_assertThisInitialized(_this), "getResultsText", function () {
var supportedTypes = _this.props.supportedTypes;
var _this$state2 = _this.state,
isLoading = _this$state2.isLoading,
searchQuery = _this$state2.searchQuery;
var resultCount = _this.getAvailableEntities().length;
var summaryNoun = supportedTypes.length > 1 ? "entities" : "".concat(supportedTypes[0], "s");
var summary = "Recently created ".concat(summaryNoun);
if (isLoading) {
summary = "Searching for \"".concat(summaryNoun, "\" matching \"").concat(searchQuery, "\"");
} else if (searchQuery) {
if (resultCount) {
summary = "Found ".concat(resultCount, " ").concat(summaryNoun, " matching \"").concat(searchQuery, "\"");
} else {
summary = "No ".concat(summaryNoun, " found matching \"").concat(searchQuery, "\"");
}
}
return {
summary: summary
};
});
_defineProperty(_assertThisInitialized(_this), "verifyResultOrThrow", function (results) {
if (!Array.isArray(results)) {
throw new Error("SearchPicker props.searchFunction resolved with a value that is not an Array.");
}
});
_defineProperty(_assertThisInitialized(_this), "renderInput", function () {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _this$props4 = _this.props,
handleKeyDown = _this$props4.handleKeyDown,
supportedTypes = _this$props4.supportedTypes;
var searchQuery = _this.state.searchQuery;
var _assertThisInitialize = _assertThisInitialized(_this),
handleOnInput = _assertThisInitialize.handleOnInput;
var placeholderNoun = supportedTypes.map(function (i) {
return "".concat(i, "s");
}).join(",");
return React.createElement(Input, _extends({
isFilter: true,
onChange: handleOnInput,
placeholder: "Search for ".concat(placeholderNoun),
testSection: "filter-".concat(placeholderNoun, "-input"),
type: "search",
value: searchQuery,
onKeyDown: handleKeyDown
}, props));
});
return _this;
}
_createClass(SearchPicker, [{
key: "render",
/**
* A simple render method that passes relevant state and controls to the
* children.
* @returns {React.Component}
*/
value: function render() {
var _this$props5 = this.props,
children = _this$props5.children,
currentFauxFocusIndex = _this$props5.currentFauxFocusIndex;
var _this$state3 = this.state,
searchQuery = _this$state3.searchQuery,
isLoading = _this$state3.isLoading;
var renderInput = this.renderInput,
handleOnInputDebounced = this.handleOnInputDebounced;
var availableEntities = this.getAvailableEntities();
return children({
availableEntities: availableEntities,
isLoading: isLoading,
renderInput: renderInput,
searchQuery: searchQuery,
makeSearchRequest: handleOnInputDebounced,
currentFauxFocusIndex: currentFauxFocusIndex,
resultsText: this.getResultsText()
});
}
}]);
return SearchPicker;
}(React.Component), _defineProperty(_class2, "propTypes", {
/**
* A render function that is invoked with the following info:
* availableEntities {Array<Entity>} - The list of entities available
* based on the current input. If no query term is
* entered, the default search collection is used.
*
* isLoading {Boolean} - Whether or not a search is in progress.
* renderInput {Function<props>} - A function to render the <Input>
* searchQuery {String} - The current search term
* currentFauxFocusIndex {Number} - Index of the item currently selected.
* resultsText {Object} - An object containing search result
* summaries based on the the types provided
* See this.getResultsText
*
*/
children: PropTypes.func.isRequired,
/**
* Which index in the list is currently selected.
* via @keyboardTracker
*/
currentFauxFocusIndex: PropTypes.number.isRequired,
/**
* This function can be set when custom search results filtering is required
* It will override the internal search results filtering
*
* @param {Array} searchResults
* @param {Array} selectedEntityIds
* @param {Array} filteredSearchResults
*/
filterAvailableEntities: PropTypes.func,
/**
* A handler for the input element event onKeyDown
* via @keyboardTracker
*/
handleKeyDown: PropTypes.func.isRequired,
/**
* The element ref to use for capturing keyboard input.
* via @keyboardTracker
*/
keyboardRef: PropTypes.shape({
current: PropTypes.elementType
}),
/**
* Handler to call when an item is selected via the keyboard.
*/
onItemSelected: PropTypes.func.isRequired,
/**
* The function to invoke to execute an authenticated public API search.
* Different consumers will have different methods of authenticating.
*
* @param {Object} requestConfig
* @return {Promise}
*/
searchFunction: PropTypes.func.isRequired,
/**
* Additional options to pass to the search function when a search is
* executed. Type and query will be overridden by this component.
*/
searchOptions: PropTypes.object,
/**
* An optional list of ids which should be considered "selected".
* If provided, these ids will be filtered out of the list.
*/
selectedEntityIds: PropTypes.array,
/**
* Sets the number of items for the list index tracker.
* via @keyboardTracker
*/
setItemCount: PropTypes.func,
/**
* Sets the item select callback fn for the list index tracker.
* via @keyboardTracker
*/
setOnItemSelect: PropTypes.func,
/* The entity types we should query search for. */
supportedTypes: PropTypes.array.isRequired
}), _defineProperty(_class2, "defaultProps", {
searchOptions: {},
selectedEntityIds: [],
setItemCount: noop,
setOnItemSelect: noop
}), _temp)) || _class;
export default SearchPicker;