UNPKG

cspace-ui

Version:
689 lines (574 loc) 21.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _reactIntl = require("react-intl"); var _get = _interopRequireDefault(require("lodash/get")); var _immutable = _interopRequireDefault(require("immutable")); var _qs = _interopRequireDefault(require("qs")); var _CheckboxInput = _interopRequireDefault(require("cspace-input/lib/components/CheckboxInput")); var _ErrorPage = _interopRequireDefault(require("./ErrorPage")); var _RelateButton = _interopRequireDefault(require("../record/RelateButton")); var _Pager = _interopRequireDefault(require("../search/Pager")); var _SearchResultSidebar = _interopRequireDefault(require("../search/SearchResultSidebar")); var _SearchResultSummary = _interopRequireDefault(require("../search/SearchResultSummary")); var _SearchResultTitleBar = _interopRequireDefault(require("../search/SearchResultTitleBar")); var _SelectBar = _interopRequireDefault(require("../search/SelectBar")); var _WatchedSearchResultTableContainer = _interopRequireDefault(require("../../containers/search/WatchedSearchResultTableContainer")); var _SearchResultSidebarToggleButtonContainer = _interopRequireDefault(require("../../containers/search/SearchResultSidebarToggleButtonContainer")); var _SearchToRelateModalContainer = _interopRequireDefault(require("../../containers/search/SearchToRelateModalContainer")); var _permissionHelpers = require("../../helpers/permissionHelpers"); var _searchHelpers = require("../../helpers/searchHelpers"); var _searchNames = require("../../constants/searchNames"); var _configHelpers = require("../../helpers/configHelpers"); var _SearchResultPage = _interopRequireDefault(require("../../../styles/cspace-ui/SearchResultPage.css")); var _PageBody = _interopRequireDefault(require("../../../styles/cspace-ui/PageBody.css")); var _SidebarToggleBar = _interopRequireDefault(require("../../../styles/cspace-ui/SidebarToggleBar.css")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } /* global window */ // FIXME: Make default page size configurable const defaultPageSize = 20; // const stopPropagation = (event) => { // event.stopPropagation(); // }; const messages = (0, _reactIntl.defineMessages)({ relate: { "id": "searchResultPage.relate", "defaultMessage": "Relate\u2026" } }); const propTypes = { isSidebarOpen: _propTypes.default.bool, history: _propTypes.default.object, location: _propTypes.default.object, match: _propTypes.default.object, perms: _propTypes.default.instanceOf(_immutable.default.Map), preferredPageSize: _propTypes.default.number, search: _propTypes.default.func, selectedItems: _propTypes.default.instanceOf(_immutable.default.Map), setPreferredPageSize: _propTypes.default.func, setSearchPageAdvanced: _propTypes.default.func, setSearchPageKeyword: _propTypes.default.func, setAllItemsSelected: _propTypes.default.func, onItemSelectChange: _propTypes.default.func }; const defaultProps = { isSidebarOpen: true }; const contextTypes = { config: _propTypes.default.object.isRequired }; class SearchResultPage extends _react.Component { constructor() { super(); this.getSearchToRelateSubjects = this.getSearchToRelateSubjects.bind(this); this.handleCheckboxClick = this.handleCheckboxClick.bind(this); this.handleCheckboxCommit = this.handleCheckboxCommit.bind(this); this.handleEditSearchLinkClick = this.handleEditSearchLinkClick.bind(this); this.handleModalCancelButtonClick = this.handleModalCancelButtonClick.bind(this); this.handleModalCloseButtonClick = this.handleModalCloseButtonClick.bind(this); this.handlePageChange = this.handlePageChange.bind(this); this.handlePageSizeChange = this.handlePageSizeChange.bind(this); this.handleRelateButtonClick = this.handleRelateButtonClick.bind(this); this.handleRelationsCreated = this.handleRelationsCreated.bind(this); this.handleSortChange = this.handleSortChange.bind(this); this.renderCheckbox = this.renderCheckbox.bind(this); this.renderFooter = this.renderFooter.bind(this); this.renderHeader = this.renderHeader.bind(this); this.search = this.search.bind(this); this.shouldShowCheckbox = this.shouldShowCheckbox.bind(this); this.state = { isSearchToRelateModalOpen: false }; } componentDidMount() { if (!this.normalizeQuery()) { const { location, setPreferredPageSize } = this.props; if (setPreferredPageSize) { const { search } = location; const query = _qs.default.parse(search.substring(1)); setPreferredPageSize(parseInt(query.size, 10)); } this.search(); } } componentDidUpdate(prevProps) { const { location, match, perms } = this.props; const { location: prevLocation, match: prevMatch, perms: prevPerms } = prevProps; const { params } = match; const { params: prevParams } = prevMatch; if (perms !== prevPerms || params.recordType !== prevParams.recordType || params.vocabulary !== prevParams.vocabulary || params.csid !== prevParams.csid || params.subresource !== prevParams.subresource || location.search !== prevLocation.search) { if (!this.normalizeQuery()) { const { setPreferredPageSize } = this.props; if (setPreferredPageSize) { const { search } = location; const query = _qs.default.parse(search.substring(1)); setPreferredPageSize(parseInt(query.size, 10)); } this.search(); } } } getListType(searchDescriptor) { const { config } = this.context; return (0, _searchHelpers.getListType)(config, searchDescriptor); } getSearchDescriptor() { // FIXME: Refactor this into a wrapper component that calculates the search descriptor from // location and params, and passes it into a child. This will eliminate the multiple calls to // this method from the various render methods in this class. const { location, match } = this.props; const { params } = match; const { search } = location; const query = _qs.default.parse(search.substring(1)); const searchQuery = Object.assign({}, query, { p: parseInt(query.p, 10) - 1, size: parseInt(query.size, 10) }); const advancedSearchCondition = query.as; if (advancedSearchCondition) { searchQuery.as = JSON.parse(advancedSearchCondition); } const searchDescriptor = { searchQuery }; ['recordType', 'vocabulary', 'csid', 'subresource'].forEach(param => { const value = params[param]; if (typeof value !== 'undefined') { searchDescriptor[param] = value; } }); return _immutable.default.fromJS(searchDescriptor); } getSearchToRelateSubjects() { const { selectedItems } = this.props; const { config } = this.context; if (!selectedItems) { return null; } const searchDescriptor = this.getSearchDescriptor(); const recordType = searchDescriptor.get('recordType'); const serviceType = (0, _get.default)(config, ['recordTypes', recordType, 'serviceConfig', 'serviceType']); const itemRecordType = serviceType === 'utility' ? undefined : recordType; const titleColumnName = (0, _configHelpers.getFirstColumnName)(config, recordType); return selectedItems.valueSeq().map(item => ({ csid: item.get('csid'), recordType: itemRecordType || (0, _configHelpers.getRecordTypeNameByServiceObjectName)(config, item.get('docType')), title: item.get(titleColumnName) })).toJS(); } isResultRelatable(searchDescriptor) { const { config } = this.context; const recordType = searchDescriptor.get('recordType'); const subresource = searchDescriptor.get('subresource'); const serviceType = (0, _get.default)(config, ['recordTypes', recordType, 'serviceConfig', 'serviceType']); return subresource !== 'terms' && (serviceType === 'procedure' || serviceType === 'object' || recordType === 'procedure' || recordType === 'object'); } closeModal() { this.setState({ isSearchToRelateModalOpen: false }); } normalizeQuery() { const { history, location, preferredPageSize } = this.props; const { search } = location; const query = _qs.default.parse(search.substring(1)); if (history) { const normalizedQueryParams = {}; const pageSize = parseInt(query.size, 10); if (isNaN(pageSize) || pageSize < 1) { const normalizedPageSize = preferredPageSize || defaultPageSize; normalizedQueryParams.size = normalizedPageSize.toString(); } else if (pageSize > 2500) { // Services layer max is 2500 normalizedQueryParams.size = '2500'; } else if (pageSize.toString() !== query.size) { normalizedQueryParams.size = pageSize.toString(); } const pageNum = parseInt(query.p, 10); if (isNaN(pageNum) || pageNum < 1) { normalizedQueryParams.p = '1'; } else if (pageNum.toString() !== query.p) { normalizedQueryParams.p = pageNum.toString(); } if (Object.keys(normalizedQueryParams).length > 0) { const newQuery = Object.assign({}, query, normalizedQueryParams); const queryString = _qs.default.stringify(newQuery); history.replace({ pathname: location.pathname, search: "?".concat(queryString) }); return true; } } return false; } shouldShowCheckbox(item) { const { perms } = this.props; const { config } = this.context; return item.get('workflowState') !== 'locked' && (0, _permissionHelpers.canRelate)((0, _configHelpers.getRecordTypeNameByUri)(config, item.get('uri')), perms, config); } handleCheckboxClick(event) { // DRYD-252: Elaborate workaround for Firefox. When a checkbox is a child of an a, clicking on // the checkbox navigates to the link. So we have to handle the checkbox click, and prevent the // default. This prevents the navigation, but also prevents the checkbox state from changing. // So we also have to manually commit the change. The Firefox bug has been open for 17 years // now. https://bugzilla.mozilla.org/show_bug.cgi?id=62151 event.preventDefault(); event.stopPropagation(); const checkbox = event.currentTarget.querySelector('input[type="checkbox"]'); const index = checkbox.dataset.name; window.setTimeout(() => { this.handleCheckboxCommit([index], !checkbox.checked); }, 0); } handleCheckboxCommit(path, value) { const index = parseInt(path[0], 10); const selected = value; const { onItemSelectChange } = this.props; const { config } = this.context; if (onItemSelectChange) { const searchDescriptor = this.getSearchDescriptor(); const listType = this.getListType(searchDescriptor); onItemSelectChange(config, _searchNames.SEARCH_RESULT_PAGE_SEARCH_NAME, searchDescriptor, listType, index, selected); } } handleEditSearchLinkClick() { // Transfer this search descriptor's search criteria to advanced search. const { setSearchPageAdvanced, setSearchPageKeyword } = this.props; if (setSearchPageKeyword || setSearchPageAdvanced) { const searchDescriptor = this.getSearchDescriptor(); const searchQuery = searchDescriptor.get('searchQuery'); if (setSearchPageKeyword) { setSearchPageKeyword(searchQuery.get('kw')); } if (setSearchPageAdvanced) { setSearchPageAdvanced(searchQuery.get('as')); } } } handleModalCancelButtonClick() { this.closeModal(); } handleModalCloseButtonClick() { this.closeModal(); } handlePageChange(pageNum) { const { history, location } = this.props; if (history) { const { search } = location; const query = _qs.default.parse(search.substring(1)); query.p = (pageNum + 1).toString(); const queryString = _qs.default.stringify(query); history.push({ pathname: location.pathname, search: "?".concat(queryString) }); } } handlePageSizeChange(pageSize) { const { history, location, setPreferredPageSize } = this.props; if (setPreferredPageSize) { setPreferredPageSize(pageSize); } if (history) { const { search } = location; const query = _qs.default.parse(search.substring(1)); query.p = '1'; query.size = pageSize.toString(); const queryString = _qs.default.stringify(query); history.push({ pathname: location.pathname, search: "?".concat(queryString) }); } } handleRelateButtonClick() { this.setState({ isSearchToRelateModalOpen: true }); } handleRelationsCreated() { this.closeModal(); } handleSortChange(sort) { const { history, location } = this.props; if (history) { const { search } = location; const query = _qs.default.parse(search.substring(1)); query.sort = sort; const queryString = _qs.default.stringify(query); history.push({ pathname: location.pathname, search: "?".concat(queryString) }); } } search() { const { search } = this.props; const { config } = this.context; const searchDescriptor = this.getSearchDescriptor(); const listType = this.getListType(searchDescriptor); if (search) { search(config, _searchNames.SEARCH_RESULT_PAGE_SEARCH_NAME, searchDescriptor, listType); } } renderCheckbox(_ref) { let { rowData, rowIndex } = _ref; const { selectedItems } = this.props; if (this.shouldShowCheckbox(rowData)) { const itemCsid = rowData.get('csid'); const selected = selectedItems ? selectedItems.has(itemCsid) : false; return _react.default.createElement(_CheckboxInput.default, { embedded: true, name: "".concat(rowIndex), value: selected // DRYD-252: Elaborate workaround for Firefox, part II. Use this onClick instead of the // onCommit and onClick below. , onClick: this.handleCheckboxClick // onCommit={this.handleCheckboxCommit} // Prevent clicking on the checkbox from selecting the record. // onClick={stopPropagation} }); } return null; } renderHeader(_ref2) { let { searchError, searchResult } = _ref2; const { selectedItems, setAllItemsSelected } = this.props; const { config } = this.context; const searchDescriptor = this.getSearchDescriptor(); const listType = this.getListType(searchDescriptor); let selectBar; if (!searchError) { const selectedCount = selectedItems ? selectedItems.size : 0; let relateButton; if (this.isResultRelatable(searchDescriptor)) { relateButton = _react.default.createElement(_RelateButton.default, { disabled: selectedCount < 1, key: "relate", label: _react.default.createElement(_reactIntl.FormattedMessage, messages.relate), name: "relate", onClick: this.handleRelateButtonClick }); } selectBar = _react.default.createElement(_SelectBar.default, { buttons: [relateButton], config: config, listType: listType, searchDescriptor: searchDescriptor, searchName: _searchNames.SEARCH_RESULT_PAGE_SEARCH_NAME, searchResult: searchResult, selectedItems: selectedItems, setAllItemsSelected: setAllItemsSelected, showCheckboxFilter: this.shouldShowCheckbox }); } return _react.default.createElement("header", null, _react.default.createElement(_SearchResultSummary.default, { config: config, listType: listType, searchDescriptor: searchDescriptor, searchError: searchError, searchResult: searchResult, onEditSearchLinkClick: this.handleEditSearchLinkClick, onPageSizeChange: this.handlePageSizeChange }), selectBar); } renderFooter(_ref3) { let { searchResult } = _ref3; if (searchResult) { const { config } = this.context; const searchDescriptor = this.getSearchDescriptor(); const listType = this.getListType(searchDescriptor); const listTypeConfig = config.listTypes[listType]; const { listNodeName } = listTypeConfig; const list = searchResult.get(listNodeName); const totalItems = parseInt(list.get('totalItems'), 10); const pageNum = parseInt(list.get('pageNum'), 10); const pageSize = parseInt(list.get('pageSize'), 10); const lastPage = Math.max(0, isNaN(totalItems) ? 0 : Math.ceil(totalItems / pageSize) - 1); return _react.default.createElement("footer", null, _react.default.createElement(_Pager.default, { currentPage: pageNum, lastPage: lastPage, pageSize: pageSize, onPageChange: this.handlePageChange, onPageSizeChange: this.handlePageSizeChange })); } return null; } render() { const { history, isSidebarOpen, selectedItems } = this.props; const { config } = this.context; const { isSearchToRelateModalOpen } = this.state; const searchDescriptor = this.getSearchDescriptor(); const advancedSearchCondition = searchDescriptor.getIn(['searchQuery', 'as']); const listType = this.getListType(searchDescriptor); const recordType = searchDescriptor.get('recordType'); const vocabulary = searchDescriptor.get('vocabulary'); const csid = searchDescriptor.get('csid'); const subresource = searchDescriptor.get('subresource'); const validation = (0, _configHelpers.validateLocation)(config, { recordType, vocabulary, csid, subresource }); if (validation.error) { return _react.default.createElement(_ErrorPage.default, { error: validation.error }); } const isResultRelatable = this.isResultRelatable(searchDescriptor); let searchToRelateModal; if (isResultRelatable) { searchToRelateModal = _react.default.createElement(_SearchToRelateModalContainer.default, { allowedServiceTypes: ['object', 'procedure'], subjects: this.getSearchToRelateSubjects, config: config, isOpen: isSearchToRelateModalOpen, defaultRecordTypeValue: "collectionobject", onCancelButtonClick: this.handleModalCancelButtonClick, onCloseButtonClick: this.handleModalCloseButtonClick, onRelationsCreated: this.handleRelationsCreated }); } return _react.default.createElement("div", { className: _SearchResultPage.default.common }, _react.default.createElement(_SearchResultTitleBar.default, { config: config, searchDescriptor: searchDescriptor, searchName: _searchNames.SEARCH_RESULT_PAGE_SEARCH_NAME, updateDocumentTitle: true }), _react.default.createElement("div", { className: advancedSearchCondition ? _SidebarToggleBar.default.advanced : _SidebarToggleBar.default.normal }, _react.default.createElement(_SearchResultSidebarToggleButtonContainer.default, null)), _react.default.createElement("div", { className: isSidebarOpen ? _PageBody.default.common : _PageBody.default.full }, _react.default.createElement(_WatchedSearchResultTableContainer.default, { config: config, history: history, listType: listType, searchName: _searchNames.SEARCH_RESULT_PAGE_SEARCH_NAME, searchDescriptor: searchDescriptor, recordType: recordType, showCheckboxColumn: true, renderCheckbox: this.renderCheckbox, renderHeader: this.renderHeader, renderFooter: this.renderFooter, onSortChange: this.handleSortChange, search: this.search }), _react.default.createElement(_SearchResultSidebar.default, { config: config, history: history, isOpen: isSidebarOpen, recordType: recordType, selectedItems: selectedItems })), searchToRelateModal); } } exports.default = SearchResultPage; SearchResultPage.propTypes = propTypes; SearchResultPage.defaultProps = defaultProps; SearchResultPage.contextTypes = contextTypes;