From de63e562bdebd32289da2e23f452e1754014a566 Mon Sep 17 00:00:00 2001 From: vbojilova Date: Fri, 1 Dec 2023 15:39:41 +1300 Subject: [PATCH] porting over accordion style search results WIP - #525 --- app/app.js | 6 +- app/containers/Search/index.js | 388 +++++++++++++++++------------ app/containers/Search/selectors.js | 22 +- app/themes/theme-base.js | 106 +++++++- package.json | 2 + 5 files changed, 346 insertions(+), 178 deletions(-) diff --git a/app/app.js b/app/app.js index 0a0e341d0..721400e61 100644 --- a/app/app.js +++ b/app/app.js @@ -26,7 +26,7 @@ import { makeSelectLocationState } from 'containers/App/selectors'; import LanguageProvider from 'containers/LanguageProvider'; // Import ThemeProvider -import { ThemeProvider } from 'styled-components'; +import { Grommet } from 'grommet'; import theme from 'themes/theme-base'; // Load the favicon, and the .htaccess file @@ -75,12 +75,12 @@ const render = (messages) => { ReactDOM.render( - + - + , document.getElementById('app'), diff --git a/app/containers/Search/index.js b/app/containers/Search/index.js index d4b3923b7..f91089948 100644 --- a/app/containers/Search/index.js +++ b/app/containers/Search/index.js @@ -22,30 +22,34 @@ import { import { CONTENT_LIST, VIEWPORTS } from 'containers/App/constants'; import Button from 'components/buttons/Button'; -import ContainerWithSidebar from 'components/styled/Container/ContainerWithSidebar'; +import ContainerWrapper from 'components/styled/Container/ContainerWrapper'; import Container from 'components/styled/Container'; import Loading from 'components/Loading'; import ContentHeader from 'components/ContentHeader'; -import EntityListSidebarLoading from 'components/EntityListSidebarLoading'; +// import EntityListSidebarLoading from 'components/EntityListSidebarLoading'; import TagSearch from 'components/TagSearch'; -import Scrollable from 'components/styled/Scrollable'; -import Sidebar from 'components/styled/Sidebar'; -import SidebarHeader from 'components/styled/SidebarHeader'; +// import Scrollable from 'components/styled/Scrollable'; import SidebarGroupLabel from 'components/styled/SidebarGroupLabel'; -import SupTitle from 'components/SupTitle'; -import Component from 'components/styled/Component'; +// import SupTitle from 'components/SupTitle'; +// mport Component from 'components/styled/Component'; import Content from 'components/styled/Content'; -import PrintHide from 'components/styled/PrintHide'; +// import PrintHide from 'components/styled/PrintHide'; +import { Box, Text } from 'grommet'; + +import { FormUp, FormDown } from 'grommet-icons'; + +import qe from 'utils/quasi-equals'; // import EntityListItem from 'components/EntityListItem'; -import EntityListHeader from 'components/EntityListMain/EntityListGroups/EntityListHeader'; -import EntityListItemWrapper from 'components/EntityListMain/EntityListGroups/EntityListItems/EntityListItemWrapper'; +// import EntityListHeader from 'components/EntityListMain/EntityListGroups/EntityListHeader'; +// import EntityListItemWrapper from 'components/EntityListMain/EntityListGroups/EntityListItems/EntityListItemWrapper'; +import EntityListItem from 'components/EntityListItem'; import appMessages from 'containers/App/messages'; // import { ROUTES } from 'containers/App/constants'; import { DEPENDENCIES } from './constants'; -import { selectEntitiesByQuery } from './selectors'; +import { selectEntitiesByQuery, selectPathQuery } from './selectors'; import { updateQuery, resetSearchQuery, @@ -68,9 +72,9 @@ const Group = styled.div` } `; -const ScrollableWrapper = styled(Scrollable)` +/* const ScrollableWrapper = styled(Scrollable)` background-color: ${palette('aside', 0)}; -`; +`; */ // TODO compare EntityListSidebarOption const Target = styled(Button)` @@ -154,9 +158,9 @@ const ListHint = styled.div` padding-bottom: 10px; `; const ListWrapper = styled.div``; -const ListEntitiesMain = styled.div` +/* const ListEntitiesMain = styled.div` padding-top: 0.5em; -`; +`; */ const TargetsMobile = styled.div` padding-bottom: 20px; `; @@ -196,7 +200,7 @@ export class Search extends React.PureComponent { // eslint-disable-line react/p return appMessages.entities.taxonomies[target.get('taxId')]; } return appMessages.entities[target.get('path')]; - } + }; resize = () => { // reset @@ -217,49 +221,54 @@ export class Search extends React.PureComponent { // eslint-disable-line react/p this.setState({ viewport }); } - renderSearchTargets = (includeEmpty = true) => { - const { intl } = this.context; - return ( -
- { this.props.entities && this.props.entities.map((group) => ( - - { includeEmpty - && ( - - - - ) - } -
- { - group.get('targets') && group.get('targets').entrySeq().map(([i, target]) => (includeEmpty || target.get('results').size > 0 || target.get('active')) && ( - { - if (evt !== undefined && evt.preventDefault) evt.preventDefault(); - this.props.onTargetSelect(target.get('path')); - }} - active={target.get('active')} - disabled={target.get('results').size === 0} - > - - {this.getTargetTitle(target) && intl.formatMessage(this.getTargetTitle(target).pluralLong || this.getTargetTitle(target).plural)} - - - - {target.get('results').size} - - - - )) - } -
-
- ))} -
- ); + resize = () => { + // reset + this.setState(STATE_INITIAL); + this.updateViewport(); + this.forceUpdate(); }; + renderSearchTargets = (includeEmpty = true) => ( +
+ {this.props.entities && this.props.entities.map((group) => ( + + {includeEmpty + && ( + + + + ) + } +
+ { + group.get('targets') && group.get('targets').entrySeq().map(([i, target]) => (includeEmpty || target.get('results').size > 0 || target.get('active')) && ( + { + if (evt !== undefined && evt.preventDefault) evt.preventDefault(); + this.props.onTargetSelect(target.get('path')); + }} + active={target.get('active')} + disabled={target.get('results').size === 0} + > + + {this.getTargetTitle(target) && this.context.intl.formatMessage(this.getTargetTitle(target).pluralLong || this.getTargetTitle(target).plural)} + + + + {target.get('results').size} + + + + )) + } +
+
+ ))} +
+ ); + + render() { const { intl } = this.context; const { @@ -269,12 +278,32 @@ export class Search extends React.PureComponent { // eslint-disable-line react/p onClear, entities, onEntityClick, - onSortOrder, - onSortBy, + activeTargetPath, } = this.props; + const hasQuery = !!location.query.search; + const countResults = dataReady && hasQuery && entities && entities.reduce( + (memo, group) => group.get('targets').reduce( + (memo2, target) => target.get('results') + ? memo2 + target.get('results').size + : memo2, + memo, + ), + 0 + ); + const countTargets = dataReady && hasQuery && entities && entities.reduce( + (memo, group) => group.get('targets').reduce( + (memo2, target) => { + if (target.get('results') && target.get('results').size > 0) { + return memo2 + 1; + } + return memo2; + }, + memo, + ), + 0, + ); const activeTarget = entities.reduce((memo, group) => group.get('targets').find((target) => target.get('active')) || memo, Map()); - const hasResults = location.query.search && activeTarget.get('results') && activeTarget.get('results').size > 0; @@ -303,28 +332,7 @@ export class Search extends React.PureComponent { // eslint-disable-line react/p { name: 'description', content: intl.formatMessage(messages.metaDescription) }, ]} /> - { !dataReady - && - } - { dataReady && this.state.viewport && this.state.viewport !== VIEWPORTS.MOBILE - && ( - - - - - - - - { - this.renderSearchTargets(true) - } - - - - - ) - } - + - { !dataReady - && - } - { dataReady - && ( -
- - onClear(['search'])} - /> - - - { - noEntry && ( - - - - ) - } - { - noResultsNoAlternative && ( - - - - ) - } - { - noResults && !noResultsNoAlternative && ( - - - - ) - } - { !noEntry && this.state.viewport && this.state.viewport === VIEWPORTS.MOBILE + {!dataReady && } + {dataReady && ( +
+ + onClear(['search'])} + /> + + + { + noEntry && ( + + + + ) + } + { + noResultsNoAlternative && ( + + + + ) + } + { + noResults && !noResultsNoAlternative && ( + + + + ) + } + {!noEntry && this.state.viewport && this.state.viewport === VIEWPORTS.MOBILE && ( - { !noResults + {!noResults && ( @@ -386,49 +391,108 @@ export class Search extends React.PureComponent { // eslint-disable-line react/p } ) - } - { hasResults - && ( -
- { this.state.viewport && this.state.viewport === VIEWPORTS.MOBILE - && ( - - - - ) + } + {hasResults && ( + + + + {`${countResults} ${countResults === 1 ? 'result' : 'results'} found in database. `} + + {countTargets > 1 && ( + + Please select a content type below to see individual results + + )} + + {entities.map( + (group, id) => { + const hasGroupResults = group.get('targets').some( + (target) => target.get('results') && target.get('results').size > 0 + ); + if (hasGroupResults) { + return ( + + + + + + + + {group.get('targets') && group.get('targets').map( + (target) => { + const hasTargetResults = target.get('results') && target.get('results').size > 0; + if (hasTargetResults) { + const count = target.get('results').size; + const title = this.getTargetTitle(target, count, intl); + const active = qe(target.get('path'), activeTargetPath); + const otherTargets = countTargets > 1; + return ( + + + { + if (evt !== undefined && evt.preventDefault) evt.preventDefault(); + if (active) { + this.props.onTargetSelect(''); + } else { + this.props.onTargetSelect(target.get('path')); + } + }} + active={active} + > + + + + {count} + + + + + + {otherTargets && active && ( + + )} + {otherTargets && !active && ( + + )} + + + + { + (active || !otherTargets) && ( + + {target.get('results').toList().map((entity, key) => ( + + ))} + + ) + } + + ); + } + return null; + } + )} + + + ); + } + return null; } - - - { activeTarget.get('results').map((entity, key) => ( - - ))} - -
- ) - } -
-
- ) - } + )} + + )} +
+
+ )}
-
+ ); } @@ -445,6 +509,7 @@ Search.propTypes = { onEntityClick: PropTypes.func.isRequired, onSortOrder: PropTypes.func.isRequired, onSortBy: PropTypes.func.isRequired, + activeTargetPath: PropTypes.string, theme: PropTypes.object, }; @@ -455,6 +520,7 @@ Search.contextTypes = { const mapStateToProps = (state, props) => ({ dataReady: selectReady(state, { path: DEPENDENCIES }), entities: selectEntitiesByQuery(state, fromJS(props.location.query)), + activeTargetPath: selectPathQuery(state), }); function mapDispatchToProps(dispatch) { return { diff --git a/app/containers/Search/selectors.js b/app/containers/Search/selectors.js index d12feb2a5..979b5dcb4 100644 --- a/app/containers/Search/selectors.js +++ b/app/containers/Search/selectors.js @@ -19,7 +19,7 @@ import { sortEntities, getSortOption } from 'utils/sort'; import { CONFIG } from './constants'; -const selectPathQuery = createSelector( +export const selectPathQuery = createSelector( (state, locationQuery) => locationQuery, (locationQuery) => locationQuery && locationQuery.get('path') ); @@ -105,11 +105,11 @@ export const selectEntitiesByQuery = createSelector( // if filtered by path if ( path === fwTargetPath - || ( - !path - && !active - && filteredEntities.size > 0 - ) + || ( + !path + && !active + && filteredEntities.size > 0 + ) ) { active = true; // only sort the active entities that will be displayed @@ -140,11 +140,11 @@ export const selectEntitiesByQuery = createSelector( // if filtered by path if ( path === target.get('path') - || ( - !path - && !active - && filteredEntities.size > 0 - ) + || ( + !path + && !active + && filteredEntities.size > 0 + ) ) { active = true; // only sort the active entities that will be displayed diff --git a/app/themes/theme-base.js b/app/themes/theme-base.js index 77dff2567..fef8e5658 100644 --- a/app/themes/theme-base.js +++ b/app/themes/theme-base.js @@ -42,10 +42,95 @@ theme.media = { // grid-styles settings https://github.com/jxnblk/grid-styled theme.gutter = 20; + +export const BREAKPOINTS = { + small: { + min: 0, + max: 768, // inclusive + name: 'mobile', + index: 0, + }, + // ms: { + // min: 420, // exclusive + // max: 720, + // name: 'mobile (landscape)', + // index: 1, + // }, + medium: { + min: 768, // exclusive + max: 992, + name: 'tablet (portrait)', + index: 2, + }, + large: { + min: 992, // exclusive + max: 1152, + name: 'laptop/tablet (landscape)', + index: 3, + }, + xlarge: { + min: 1152, // exclusive + max: 99999999, + name: 'desktop', + index: 4, + }, + // xxlarge: { + // min: 1728, // exclusive + // max: 99999999, + // name: 'large desktop', + // index: 5, + // }, +}; + theme.breakpoints = { - small: '769px', - medium: '993px', - large: '1200px', + small: `${BREAKPOINTS.small.min}px`, // max + // ms: `${BREAKPOINTS.ms.min}px`, // max + medium: `${BREAKPOINTS.medium.min}px`, // min + large: `${BREAKPOINTS.large.min}px`, // min + xlarge: `${BREAKPOINTS.xlarge.min}px`, // min + // xxlarge: `${BREAKPOINTS.xxlarge.min}px`, // min +}; +theme.breakpointsMin = { + small: `${BREAKPOINTS.small.min + 1}px`, // min + medium: `${BREAKPOINTS.medium.min + 1}px`, // min + large: `${BREAKPOINTS.large.min + 1}px`, // min + // ms: `${BREAKPOINTS.ms.min + 1}px`, // min + // xlarge: `${BREAKPOINTS.xlarge.min + 1}px`, // min + // xxlarge: `${BREAKPOINTS.xxlarge.min + 1}px`, // min +}; +// grommet +theme.global = { + drop: { + zIndex: 200, + }, + breakpoints: { + small: { + value: BREAKPOINTS.small.max, + }, + medium: { + value: BREAKPOINTS.medium.max, + }, + large: { + value: BREAKPOINTS.large.max, + }, + xlarge: {}, + }, + colors: { + aHover: '#08586c', + }, + edgeSize: { + hair: '1px', + xxsmall: '3px', + xsmall: '6px', + small: '12px', + ms: '16px', + medium: '24px', + ml: '36px', + large: '48px', + xlarge: '64px', + xxlarge: '100px', + }, + }; // global color palettes @@ -362,6 +447,21 @@ theme.sizes = { }, }; +theme.text = { + xxxlarge: { size: '48px', height: '60px', maxWidth: '800px' }, + xxlarge: { size: '30px', height: '36px', maxWidth: '800px' }, + xlarge: { size: '20px', height: '28px', maxWidth: '800px' }, + large: { size: '18px', height: '24px', maxWidth: '800px' }, + largeTall: { size: '18px', height: '26px', maxWidth: '800px' }, + medium: { size: '16px', height: '21px', maxWidth: '800px' }, + mediumTall: { size: '16px', height: '23px', maxWidth: '800px' }, + mediumTight: { size: '16px', height: '18px', maxWidth: '800px' }, + small: { size: '14px', height: '18px', maxWidth: '700px' }, + xsmall: { size: '13px', height: '16px', maxWidth: '600px' }, + xxsmall: { size: '12px', height: '14px', maxWidth: '500px' }, + xxxsmall: { size: '11px', height: '13px', maxWidth: '500px' }, +}; + // end styled-theme settings // other global theme variables diff --git a/package.json b/package.json index 63c0f87ef..c0edfc121 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,8 @@ "express": "4.14.0", "fontfaceobserver": "2.0.7", "grid-styled": "^1.0.1", + "grommet": "2.10", + "grommet-icons": "4.4.0", "history": "4.9.0", "hoist-non-react-statics": "3.3.0", "immer": "9.0.6",