mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
Add SearchAttributes component
Move search summary content into the new SearchAttributes component.
This commit is contained in:
parent
c33752e8b8
commit
3a1a472a32
8 changed files with 156 additions and 160 deletions
52
src/components/SearchAttributes/SearchAttributes.css
Normal file
52
src/components/SearchAttributes/SearchAttributes.css
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.searchResultSummary {
|
||||
display: none;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
display: block;
|
||||
margin: 8px 24px 14px 24px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin: 8px 36px 14px 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.searchResultSummaryMobile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
background-color: var(--matterColorBright);
|
||||
width: 100vw;
|
||||
padding: 1px 24px 14px 24px;
|
||||
|
||||
/* sticky result summary in mobile */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mapIcon {
|
||||
/* Font */
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
font-weight: var(--fontWeightBold);
|
||||
|
||||
/* background map image */
|
||||
background-image: url(./images/map_icon@2x.png);
|
||||
background-size: cover;
|
||||
|
||||
border-radius: 3px;
|
||||
width: 72px;
|
||||
height: 35px;
|
||||
margin-top: 23px;
|
||||
padding: 9px 22px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--failColor);
|
||||
}
|
||||
66
src/components/SearchAttributes/SearchAttributes.js
Normal file
66
src/components/SearchAttributes/SearchAttributes.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import css from './SearchAttributes.css';
|
||||
|
||||
const SearchAttributes = props => {
|
||||
const { listingsAreLoaded, resultsCount, searchInProgress, searchListingsError, onMapIconClick} = props;
|
||||
|
||||
const searchError = (
|
||||
<h2 className={css.error}>
|
||||
<FormattedMessage id="SearchAttributes.searchError" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const loadingResults = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchAttributes.loadingResults" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const resultsFound = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchAttributes.foundResults" values={{ count: resultsCount }} />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const noResults = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchAttributes.noResults" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const resultsFoundMobile = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchAttributes.foundResultsMobile" values={{ count: resultsCount }} />
|
||||
</h2>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={css.searchResultSummary}>
|
||||
{searchListingsError ? searchError : null}
|
||||
{listingsAreLoaded && resultsCount > 0 ? resultsFound : null}
|
||||
{listingsAreLoaded && resultsCount === 0 ? noResults : null}
|
||||
{searchInProgress ? loadingResults : null}
|
||||
</div>
|
||||
|
||||
<div className={css.searchResultSummaryMobile}>
|
||||
<div>
|
||||
{searchListingsError ? searchError : null}
|
||||
{listingsAreLoaded && resultsCount > 0 ? resultsFoundMobile : null}
|
||||
{listingsAreLoaded && resultsCount === 0 ? noResults : null}
|
||||
{searchInProgress ? loadingResults : null}
|
||||
</div>
|
||||
<div
|
||||
className={css.mapIcon}
|
||||
onClick={onMapIconClick}
|
||||
>
|
||||
<FormattedMessage id="SearchAttributes.openMapView" className={css.mapIconText} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchAttributes;
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
|
@ -85,6 +85,7 @@ export { default as ResponsiveImage } from './ResponsiveImage/ResponsiveImage';
|
|||
export { default as ReviewModal } from './ReviewModal/ReviewModal';
|
||||
export { default as ReviewRating } from './ReviewRating/ReviewRating';
|
||||
export { default as Reviews } from './Reviews/Reviews';
|
||||
export { default as SearchAttributes } from './SearchAttributes/SearchAttributes';
|
||||
export { default as SearchMap } from './SearchMap/SearchMap';
|
||||
export { default as SearchMapGroupLabel } from './SearchMapGroupLabel/SearchMapGroupLabel';
|
||||
export { default as SearchMapInfoCard } from './SearchMapInfoCard/SearchMapInfoCard';
|
||||
|
|
|
|||
|
|
@ -45,57 +45,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.searchResultSummary {
|
||||
display: none;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
display: block;
|
||||
margin: 8px 24px 14px 24px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin: 8px 36px 14px 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.searchResultSummaryMobile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
background-color: var(--matterColorBright);
|
||||
width: 100vw;
|
||||
padding: 1px 24px 14px 24px;
|
||||
|
||||
/* sticky result summary in mobile */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mapIcon {
|
||||
/* Font */
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
font-weight: var(--fontWeightBold);
|
||||
|
||||
/* background map image */
|
||||
background-image: url(./images/map_icon@2x.png);
|
||||
background-size: cover;
|
||||
|
||||
border-radius: 3px;
|
||||
width: 72px;
|
||||
height: 35px;
|
||||
margin-top: 23px;
|
||||
padding: 9px 22px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--failColor);
|
||||
}
|
||||
|
||||
.searchString {
|
||||
/* Search string should not break on white spaces - i.e. line-break should happen before. */
|
||||
white-space: nowrap;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
|
@ -19,7 +19,13 @@ import { createSlug, parse, stringify } from '../../util/urlHelpers';
|
|||
import * as propTypes from '../../util/propTypes';
|
||||
import { getListingsById } from '../../ducks/marketplaceData.duck';
|
||||
import { manageDisableScrolling, isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { SearchMap, ModalInMobile, Page, SearchResultsPanel } from '../../components';
|
||||
import {
|
||||
SearchMap,
|
||||
ModalInMobile,
|
||||
Page,
|
||||
SearchResultsPanel,
|
||||
SearchAttributes,
|
||||
} from '../../components';
|
||||
import { TopbarContainer } from '../../containers';
|
||||
|
||||
import { searchListings, searchMapListings } from './SearchPage.duck';
|
||||
|
|
@ -172,48 +178,6 @@ export class SearchPageComponent extends Component {
|
|||
const totalItems = searchParamsMatch && hasPaginationInfo ? pagination.totalItems : 0;
|
||||
const listingsAreLoaded = !searchInProgress && searchParamsMatch && hasPaginationInfo;
|
||||
|
||||
const searchError = (
|
||||
<h2 className={css.error}>
|
||||
<FormattedMessage id="SearchPage.searchError" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const resultsFoundNoAddress = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchPage.foundResults" values={{ count: totalItems }} />
|
||||
</h2>
|
||||
);
|
||||
const addressNode = address ? (
|
||||
<span className={css.searchString}>{searchInURL.address.split(', ')[0]}</span>
|
||||
) : null;
|
||||
const resultsFoundWithAddress = (
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="SearchPage.foundResultsWithAddress"
|
||||
values={{ count: totalItems, address: addressNode }}
|
||||
/>
|
||||
</h2>
|
||||
);
|
||||
const resultsFound = address && !mapSearch ? resultsFoundWithAddress : resultsFoundNoAddress;
|
||||
|
||||
const resultsFoundMobile = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchPage.foundResultsMobile" values={{ count: totalItems }} />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const noResults = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchPage.noResults" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const loadingResults = (
|
||||
<h2>
|
||||
<FormattedMessage id="SearchPage.loadingResults" />
|
||||
</h2>
|
||||
);
|
||||
|
||||
const searchMap = (
|
||||
<SearchMap
|
||||
bounds={bounds}
|
||||
|
|
@ -264,6 +228,14 @@ export class SearchPageComponent extends Component {
|
|||
itemListElement: schemaListings,
|
||||
});
|
||||
|
||||
|
||||
const onMapIconClick = () => {
|
||||
this.useLocationSearchBounds = true;
|
||||
this.modalOpenedBoundsChange = true;
|
||||
this.setState({ isSearchMapOpenOnMobile: true });
|
||||
};
|
||||
|
||||
|
||||
// N.B. openMobileMap button is sticky.
|
||||
// For some reason, stickyness doesn't work on Safari, if the element is <button>
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
|
|
@ -283,30 +255,14 @@ export class SearchPageComponent extends Component {
|
|||
<TopbarContainer className={css.topbar} />
|
||||
<div className={css.container}>
|
||||
<div className={css.searchResultContainer}>
|
||||
<div className={css.searchResultSummary}>
|
||||
{searchListingsError ? searchError : null}
|
||||
{listingsAreLoaded && totalItems > 0 ? resultsFound : null}
|
||||
{listingsAreLoaded && totalItems === 0 ? noResults : null}
|
||||
{searchInProgress ? loadingResults : null}
|
||||
</div>
|
||||
<div className={css.searchResultSummaryMobile}>
|
||||
<div>
|
||||
{searchListingsError ? searchError : null}
|
||||
{listingsAreLoaded && totalItems > 0 ? resultsFoundMobile : null}
|
||||
{listingsAreLoaded && totalItems === 0 ? noResults : null}
|
||||
{searchInProgress ? loadingResults : null}
|
||||
</div>
|
||||
<div
|
||||
className={css.mapIcon}
|
||||
onClick={() => {
|
||||
this.useLocationSearchBounds = true;
|
||||
this.modalOpenedBoundsChange = true;
|
||||
this.setState({ isSearchMapOpenOnMobile: true });
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="SearchPage.openMapView" className={css.mapIconText} />
|
||||
</div>
|
||||
</div>
|
||||
<SearchAttributes
|
||||
listingsAreLoaded={listingsAreLoaded}
|
||||
resultsCount={totalItems}
|
||||
searchInProgress={searchInProgress}
|
||||
searchListingsError={searchListingsError}
|
||||
onMapIconClick={onMapIconClick}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={classNames(css.listings, {
|
||||
[css.newSearchInProgress]: !listingsAreLoaded,
|
||||
|
|
|
|||
|
|
@ -20,40 +20,12 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
<withRouter(Connect(TopbarContainerComponent)) />
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="SearchPage.foundResults"
|
||||
values={
|
||||
Object {
|
||||
"count": 22,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</h2>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="SearchPage.foundResultsMobile"
|
||||
values={
|
||||
Object {
|
||||
"count": 22,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
onClick={[Function]}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="SearchPage.openMapView"
|
||||
values={Object {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SearchAttributes
|
||||
listingsAreLoaded={true}
|
||||
resultsCount={22}
|
||||
searchInProgress={false}
|
||||
searchListingsError={null}
|
||||
/>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
|
|
|
|||
|
|
@ -455,17 +455,17 @@
|
|||
"ReviewModal.description": "Reviews are an important part of the Saunatime community. Please share what went well and what could have been improved.",
|
||||
"ReviewModal.later": "Later",
|
||||
"ReviewModal.title": "Leave a review for {revieweeName}",
|
||||
"SearchAttributes.foundResults": "{count, number} {count, plural, one {sauna} other {saunas}} found",
|
||||
"SearchAttributes.foundResultsMobile": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"SearchAttributes.foundResultsWithAddress": "{count, number} {count, plural, one {sauna} other {saunas}} around {address}",
|
||||
"SearchAttributes.loadingResults": "Loading search results…",
|
||||
"SearchAttributes.noResults": "Could not find any listings.",
|
||||
"SearchAttributes.openMapView": "Map",
|
||||
"SearchAttributes.searchError": "Search failed. Please try again.",
|
||||
"SearchMapInfoCard.noImage": "No image",
|
||||
"SearchPage.foundResults": "{count, number} {count, plural, one {sauna} other {saunas}} found",
|
||||
"SearchPage.foundResultsMobile": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"SearchPage.foundResultsWithAddress": "{count, number} {count, plural, one {sauna} other {saunas}} around {address}",
|
||||
"SearchPage.loadingResults": "Loading search results…",
|
||||
"SearchPage.noResults": "Could not find any listings.",
|
||||
"SearchPage.openMapView": "Map",
|
||||
"SearchPage.schemaDescription": "Showing search results",
|
||||
"SearchPage.schemaMapSearch": "map search",
|
||||
"SearchPage.schemaTitle": "Search results for {searchAddress} | {siteTitle}",
|
||||
"SearchPage.searchError": "Search failed. Please try again.",
|
||||
"SearchResultsPanel.nextPage": "Next page",
|
||||
"SearchResultsPanel.previousPage": "Previous page",
|
||||
"SectionHero.mobileSearchButtonText": "Search saunas",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue