Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0cda815
Create sort config for collectionobject
mikejritter Jan 9, 2026
b1cdd42
Prefer new sort config when getting the actual parameter
mikejritter Jan 9, 2026
d1ad354
Add render prop for creating the sort by display
mikejritter Jan 9, 2026
1f97fed
Start work on a SortBy component
mikejritter Jan 9, 2026
be1dae1
Add reusable sort handlers
mikejritter Jan 12, 2026
edf6f73
Use new handlers and pass SortBy as a render prop
mikejritter Jan 12, 2026
90e4cd9
Add messages and default fields
mikejritter Jan 12, 2026
e11aa0d
Read options from config
mikejritter Jan 12, 2026
072aa88
Add sort direction
mikejritter Jan 12, 2026
ecc809e
Add SortBy import
mikejritter Jan 12, 2026
d6854b7
Images from cspace-layout for up/down arrows
mikejritter Jan 12, 2026
ff0e33e
Add buttonBg color
mikejritter Jan 12, 2026
5ca5d75
Basic styles for ascending/descending
mikejritter Jan 12, 2026
ca64bca
Minor formatting updates
mikejritter Jan 13, 2026
6c87d50
Tests for sort handlers
mikejritter Jan 13, 2026
a9f7535
Styling for grouping right side inputs
mikejritter Jan 14, 2026
b0106e1
Styling for sort by
mikejritter Jan 14, 2026
5f92871
Use classnames instead of string template
mikejritter Jan 14, 2026
290c758
MiniButton to Button
mikejritter Jan 14, 2026
ca5856f
Add SortBy to existing search results
mikejritter Jan 14, 2026
91d2dcc
Add aria labels for sort direction
mikejritter Jan 14, 2026
a4e9227
Handle null config
mikejritter Jan 14, 2026
2709a45
Remove console log
mikejritter Jan 20, 2026
b58fe13
Update object number message for consistency
mikejritter Jan 20, 2026
944f371
Update object number message for consistency
mikejritter Jan 20, 2026
008c289
Update comment
mikejritter Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions images/collapseWhite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions images/expandWhite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/actions/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ const getSortParam = (config, searchDescriptor, columnSetName) => {
return null;
}

// prefer the new sort.js config
const sort = get(config,
['recordTypes', searchDescriptor.get('recordType'), 'sort', sortColumnName]);

if (sort && sort.sortBy) {
return sort.sortBy + (sortDir ? ' DESC' : '');
}

const column = get(config,
['recordTypes', searchDescriptor.get('recordType'), 'columns', columnSetName, sortColumnName]);

Expand Down
23 changes: 23 additions & 0 deletions src/components/pages/SearchResultPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
createPageSizeChangeHandler,
createPageChangeHandler,
extractAdvancedSearchGroupedTerms,
createSortByHandler,
createSortDirHandler,
} from '../../helpers/searchHelpers';
import { SEARCH_RESULT_PAGE_SEARCH_NAME } from '../../constants/searchNames';

Expand All @@ -30,6 +32,7 @@ import styles from '../../../styles/cspace-ui/SearchResultPage.css';
import pageBodyStyles from '../../../styles/cspace-ui/PageBody.css';
import ExportResults from '../search/ExportResults';
import RelateResults from '../search/RelateResults';
import SortBy from '../search/SortBy';

// const stopPropagation = (event) => {
// event.stopPropagation();
Expand Down Expand Up @@ -467,6 +470,8 @@ export default class SearchResultPage extends Component {

renderHeader({ searchError, searchResult }) {
const {
history,
location,
selectedItems,
setAllItemsSelected,
perms,
Expand Down Expand Up @@ -515,6 +520,23 @@ export default class SearchResultPage extends Component {
);
}

const {
search,
} = location;

const query = qs.parse(search.substring(1));
const sortChangeHandler = createSortByHandler({ history, location });
const sortDirChangeHandler = createSortDirHandler({ history, location });

const renderSortBy = () => (
<SortBy
onSortChange={sortChangeHandler}
onSortDirChange={sortDirChangeHandler}
sort={query?.sort}
recordType={searchDescriptor.get('recordType')}
/>
);

return (
<header>
<SearchResultSummary
Expand All @@ -524,6 +546,7 @@ export default class SearchResultPage extends Component {
searchDescriptor={searchDescriptor}
onEditSearchLinkClick={this.handleEditSearchLinkClick}
onPageSizeChange={this.handlePageSizeChange}
renderSortBy={() => renderSortBy()}
/>
{selectBar}
</header>
Expand Down
16 changes: 16 additions & 0 deletions src/components/pages/search/SearchResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SearchResultGrid from '../../search/grid/SearchResultGrid';
import SearchDetailList from '../../search/list/SearchList';
import SearchResultSidebar from '../../search/SearchResultSidebar';
import SearchResultSummary from '../../search/SearchResultSummary';
import SortBy from '../../search/SortBy';
import { ToggleButton, ToggleButtonContainer } from '../../search/header/ToggleButtons';
import { useConfig } from '../../config/ConfigProvider';
import styles from '../../../../styles/cspace-ui/SearchResults.css';
Expand All @@ -37,6 +38,8 @@ import {
getListTypeFromResult,
createPageSizeChangeHandler,
extractAdvancedSearchGroupedTerms,
createSortByHandler,
createSortDirHandler,
} from '../../../helpers/searchHelpers';
import {
setSearchPageAdvanced,
Expand Down Expand Up @@ -276,6 +279,18 @@ export default function SearchResults(props) {
});
};

const handleSortChange = createSortByHandler({ history, location });
const handleSortDirChange = createSortDirHandler({ history, location });

const renderSortBy = () => (
<SortBy
onSortChange={handleSortChange}
onSortDirChange={handleSortDirChange}
sort={normalizedQuery?.sort}
recordType={searchDescriptor.get('recordType')}
/>
);

const searchResults = useSelector((state) => getSearchResult(state,
SEARCH_RESULT_PAGE_SEARCH_NAME,
searchDescriptor));
Expand Down Expand Up @@ -351,6 +366,7 @@ export default function SearchResults(props) {
searchDescriptor={searchDescriptor}
onPageSizeChange={handlePageSizeChange}
onEditSearchLinkClick={handleEditSearchLinkClick}
renderSortBy={() => renderSortBy()}
/>
<SelectExportRelateToggleBar
toggleBar={displayToggles}
Expand Down
9 changes: 8 additions & 1 deletion src/components/search/SearchResultSummary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { defineMessages, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import get from 'lodash/get';
import { useSelector } from 'react-redux';
import classNames from 'classnames';
import PageSizeChooser from './PageSizeChooser';
import { ERR_API, ERR_NOT_ALLOWED } from '../../constants/errorCodes';
import styles from '../../../styles/cspace-ui/SearchResultSummary.css';
Expand Down Expand Up @@ -35,6 +36,7 @@ const propTypes = {
renderEditLink: PropTypes.func,
onEditSearchLinkClick: PropTypes.func,
onPageSizeChange: PropTypes.func,
renderSortBy: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -69,6 +71,7 @@ export default function SearchResultSummary(props) {
searchDescriptor,
searchName,
renderEditLink,
renderSortBy,
onEditSearchLinkClick,
onPageSizeChange,
} = props;
Expand Down Expand Up @@ -161,11 +164,15 @@ export default function SearchResultSummary(props) {
);

const className = isSearching ? styles.searching : styles.normal;
const groupedClassName = classNames(styles.flex, styles.flexInitial);

return (
<div className={className}>
{content}
{pageSizeChooser}
<div className={groupedClassName}>
{renderSortBy?.()}
{pageSizeChooser}
</div>
</div>
);
}
Expand Down
102 changes: 102 additions & 0 deletions src/components/search/SortBy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import PropTypes from 'prop-types';
import { baseComponents as components } from 'cspace-input';
import {
defineMessages, injectIntl, intlShape,
} from 'react-intl';

import { get } from 'lodash';
import classNames from 'classnames';
import styles from '../../../styles/cspace-ui/SortBy.css';
import { useConfig } from '../config/ConfigProvider';

const { DropdownMenuInput, Button } = components;

const messages = defineMessages({
sortBy: {
id: 'search.sortBy',
defaultMessage: 'Sort By',
},
ascendingAriaLabel: {
id: 'search.sortDir.ascending.label',
defaultMessage: 'Current sort: ascending',
},
descendingAriaLabel: {
id: 'search.sortDir.descending.label',
defaultMessage: 'Current sort: descending',
},
});

function SortBy({
intl,
onSortChange,
onSortDirChange,
recordType,
sort,
}) {
const config = useConfig();
const sortConfig = get(config, ['recordTypes', recordType, 'sort'])
?? get(config, ['recordTypes', recordType, 'columns', 'default']);

if (!sortConfig) {
return null;
}

const {
defaultSortBy = 'updatedAt',
defaultSortDir = 'desc',
} = sortConfig;

const options = Object.keys(sortConfig)
.filter((key) => sortConfig[key].sortBy !== undefined)
.map((key) => {
const option = sortConfig[key];
const label = intl.formatMessage(option.messages.label) ?? key;

return {
value: key,
label,
};
});

const [sortBy, sortDir] = sort?.split(' ') ?? [defaultSortBy, defaultSortDir];
const input = (
<DropdownMenuInput
options={options}
value={sortBy}
onCommit={(path, value) => onSortChange(value)}
/>
);

const sortDirClass = sortDir ? styles.descending : styles.ascending;
const sortDirLabel = sortDir ? intl.formatMessage(messages.descendingAriaLabel)
: intl.formatMessage(messages.ascendingAriaLabel);
const sortDirButton = (
<Button
aria-label={sortDirLabel}
className={classNames(sortDirClass, styles.sortByButton)}
onClick={() => onSortDirChange()}
/>
);

const prefixMessage = intl.formatMessage(messages.sortBy);
const prefix = <span className={classNames(styles.mt2, styles.mr5)}>{prefixMessage}</span>;

return (
<div className={styles.flex}>
{prefix}
{input}
{sortDirButton}
</div>
);
}

SortBy.propTypes = {
intl: intlShape,
onSortChange: PropTypes.func,
onSortDirChange: PropTypes.func,
recordType: PropTypes.string,
sort: PropTypes.string,
};

export default injectIntl(SortBy);
60 changes: 60 additions & 0 deletions src/helpers/searchHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1395,3 +1395,63 @@ export const extractAdvancedSearchGroupedTerms = (searchQuery) => {
limitBy: null,
};
};

/**
* Handler which updates the sortBy portion of the sort string. This preserves
* the sort direction previously applied to the query.
*/
export const createSortByHandler = ({ history, location }) => (sort) => {
if (!history || !location) {
return;
}

const { search } = location;
const query = qs.parse(search.substring(1));

if (query) {
const { sort: currentSort } = query;

const sortDir = currentSort?.split(' ')[1] ?? undefined;
query.sort = sortDir === undefined ? sort : `${sort} ${sortDir}`;
const queryString = qs.stringify(query);
history.push({
pathname: location.pathname,
search: `?${queryString}`,
state: location.state,
});
}
};

/**
* Handler which updates the sort direction portion of the sort string. This
* inverts based on the existence of the previous sort direction as undefined/null
* is used for ascending.
*
* Note: if no parameters are present, the API defaults to updatedAt descending.
*/
export const createSortDirHandler = ({
history,
location,
defaultSortBy = 'updatedAt',
defaultSortDir = 'desc',
}) => () => {
if (!history || !location) {
return;
}

const { search } = location;
const query = qs.parse(search.substring(1));

if (query) {
const { sort } = query;

const [sortColumn, sortDir] = sort?.split(' ') ?? [defaultSortBy, defaultSortDir];
query.sort = sortDir === undefined ? `${sortColumn} desc` : sortColumn;
const queryString = qs.stringify(query);
history.push({
pathname: location.pathname,
search: `?${queryString}`,
state: location.state,
});
}
};
2 changes: 2 additions & 0 deletions src/plugins/recordTypes/collectionobject/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import messages from './messages';
import optionLists from './optionLists';
import prepareForSending from './prepareForSending';
import serviceConfig from './serviceConfig';
import sort from './sort';
import title from './title';

export default () => (configContext) => ({
Expand All @@ -19,6 +20,7 @@ export default () => (configContext) => ({
messages,
prepareForSending,
serviceConfig,
sort,
advancedSearch: advancedSearch(configContext),
columns: columns(configContext),
detailList: detailList(configContext),
Expand Down
Loading