Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHK-134: Drop-down field style of Location Search autocomplete component #21

Merged
merged 32 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1ed6afd
Add popover style and decouple component
wdsrocha Jul 16, 2020
965707d
Add list style
wdsrocha Jul 16, 2020
77ae1f5
Add kind of broken Option
wdsrocha Jul 16, 2020
cb9a8c2
Fix popover style
wdsrocha Jul 16, 2020
1b57356
Fix option alignment a little bit
wdsrocha Jul 16, 2020
afa5148
Put drop-down in the right place
wdsrocha Jul 21, 2020
a643966
Use symetric padding on Option and fix icon shrink
wdsrocha Jul 21, 2020
fe3e4fa
Add styling on msg displayed when the search fails
wdsrocha Jul 21, 2020
3bc5d5c
Remove redundant truncate and make fail msg grey
wdsrocha Jul 21, 2020
dae152f
Add big addresses so we can test text overflow
wdsrocha Jul 21, 2020
1e11bbe
Shortnen spanish text so it fits the component (gracias Maxito 🇦🇷)
wdsrocha Jul 21, 2020
88b062b
Add Option bg color on hover
wdsrocha Jul 21, 2020
7127d88
Add mocked Google logo
wdsrocha Jul 21, 2020
abe1326
Add prop for engine logo
wdsrocha Jul 22, 2020
a013635
Refactor ref variable name
wdsrocha Jul 23, 2020
72e205d
Document why we clear button isn't tabbable
wdsrocha Jul 23, 2020
5156127
Stop sending props to renderEngineLogo
wdsrocha Jul 23, 2020
d2c3da5
Decouple Place Icon from Combobox component
wdsrocha Jul 23, 2020
4b3edac
Pass Place Icon as prop instead of render function
wdsrocha Jul 23, 2020
80b5967
Add complexity to mocked addresses
wdsrocha Jul 24, 2020
bb0ae9d
Add extra data and highlight to suggestion
wdsrocha Jul 24, 2020
c562b3b
Decouple data fetching logic from the component
wdsrocha Jul 24, 2020
3ad803d
Fix Place Icon color
wdsrocha Jul 24, 2020
734f0fc
Fix clear icon color on hover
wdsrocha Jul 24, 2020
e44fe25
Fix option color when active
wdsrocha Jul 25, 2020
e1fdd9a
Fix color and text width from search failure
wdsrocha Jul 28, 2020
656efdc
Increase Clear button clickable area
wdsrocha Jul 28, 2020
a2caaa2
Let component width be handled by parent
wdsrocha Jul 28, 2020
9823bf7
Simplify renderSuggestionText
wdsrocha Jul 29, 2020
94ba39f
Remove unused abstraction
wdsrocha Jul 30, 2020
2c1428d
Use styling from Tachyons
wdsrocha Jul 30, 2020
0bfa9b3
Refactor nit
wdsrocha Jul 30, 2020
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
2 changes: 1 addition & 1 deletion messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
"place-components.label.postalCode": "Código Postal",
"place-components.label.dontKnowPostalCode": "No conozco mi código postal",
"place-components.label.autocompleteAddress": "Dirección y número",
"place-components.label.autocompleteAddressFail": "No se han encontrado resultados"
"place-components.label.autocompleteAddressFail": "Sin resultados"
}
3 changes: 0 additions & 3 deletions react/LocationSearch.css

This file was deleted.

160 changes: 106 additions & 54 deletions react/LocationSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
import React, { useState, useMemo } from 'react'
import React, { useState, useMemo, useRef } from 'react'
import { FormattedMessage } from 'react-intl'
import { Input, IconSearch, IconClear } from 'vtex.styleguide'
import { Input, IconSearch, IconClear, IconWarning } from 'vtex.styleguide'
import { positionMatchWidth } from '@reach/popover'

import {
Combobox,
ComboboxInput,
ComboboxOption,
ComboboxPopover,
ComboboxList,
} from '@reach/combobox'
import '@reach/combobox/styles.css'
} from './components/Combobox'
import PlaceIcon from './components/PlaceIcon'

import { addresses as mockedAddresses } from './addresses'
import styles from './LocationSearch.css'
interface Interval {
offset: number
size: number
}

const MAX_DROPDOWN_ADDRESSES = 6
export interface Suggestion {
description: string
mainText: string
mainTextMatchInterval: Interval
secondaryText: string
}

// This function will be replaced in the future, after integrating the
// component with GraphQL queries.
const getAddresses = (searchTerm: string) => {
return mockedAddresses
.filter((address: string) =>
address.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
)
.slice(0, MAX_DROPDOWN_ADDRESSES)
const renderSuggestionText = (suggestion: Suggestion) => {
const { mainText } = suggestion
const { offset, size } = suggestion.mainTextMatchInterval
return (
<div className="truncate c-muted-2">
Copy link
Contributor Author

@wdsrocha wdsrocha Jul 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the text inside an Option is too big, it will truncate with an ellipsis, but there is a problem: the mainText (street name) is black and the secondaryText (city and state name) is gray, which would require the ellipsis color to be dynamic, depending on which part have overflowed.

Doing this at implementation level is quite painful and don't seems to bring too much value (big street names aren't very common, I guess), so I've opted to always display the ellipsis with a gray color. Is that okay? @davicosta99 @augustocb

Example of fake big street name with the grey colored ellipsis:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine, Wesley, thanks for the concern. Appreciate the level of care 😉

<span className="c-on-base">
{mainText.substr(0, offset)}
<span className="b">{mainText.substr(offset, size)}</span>
{mainText.substr(size + offset)}
</span>{' '}
<span>{suggestion.secondaryText}</span>
</div>
)
}

interface LocationSearchProps {
getAddresses: (searchTerm: string) => Suggestion[]
onSelectAddress?: (selectedAddress: string) => void
renderEngineLogo?: () => React.ReactNode
}

const LocationSearch: React.FC<LocationSearchProps> = ({ onSelectAddress }) => {
const LocationSearch: React.FC<LocationSearchProps> = ({
getAddresses,
onSelectAddress,
renderEngineLogo,
}) => {
const [searchTerm, setSearchTerm] = useState<string>('')
const addresses = useMemo(() => getAddresses(searchTerm), [searchTerm])
const addresses = useMemo(() => getAddresses(searchTerm), [
getAddresses,
searchTerm,
])
const inputWrapperRef = useRef<HTMLDivElement>(null)

const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key !== 'Escape') {
Expand All @@ -46,48 +70,76 @@ const LocationSearch: React.FC<LocationSearchProps> = ({ onSelectAddress }) => {
}

return (
<div className={`${styles.locationSearch} w-100`}>
<div className="w-100">
<Combobox onSelect={handleAddressSelection}>
<ComboboxInput
as={Input}
testId="location-search-input"
label={
<FormattedMessage id="place-components.label.autocompleteAddress" />
}
prefix={
<div className="c-action-primary flex justify-center items-center">
<IconSearch />
</div>
}
suffix={
searchTerm.trim().length && (
<span
data-testid="location-search-clear"
role="button"
tabIndex={-1}
className="pointer c-muted-3 flex justify-center items-center outline-0"
onClick={() => setSearchTerm('')}
onKeyPress={() => {}}
>
<IconClear />
</span>
<div ref={inputWrapperRef}>
<ComboboxInput
as={Input}
testId="location-search-input"
label={
<FormattedMessage id="place-components.label.autocompleteAddress" />
}
prefix={
<div className="c-action-primary flex justify-center items-center">
<IconSearch />
</div>
}
suffix={
searchTerm.trim().length && (
<span
data-testid="location-search-clear"
role="button"
// the input can be cleared by pressing the esc key,
// so the clear button should not be tabbable
tabIndex={-1}
className="flex pa3 na3 pointer outline-0 c-muted-3 hover-gray"
onClick={() => setSearchTerm('')}
onKeyPress={() => {}}
>
<IconClear />
</span>
)
}
value={searchTerm}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setSearchTerm(event.target.value)
}
onKeyDown={handleKeyDown}
/>
</div>
<ComboboxPopover
position={(_targetRect, popoverRect) =>
positionMatchWidth(
inputWrapperRef.current?.getBoundingClientRect(),
popoverRect
)
}
value={searchTerm}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setSearchTerm(event.target.value)
}
onKeyDown={handleKeyDown}
/>
<ComboboxPopover>
>
{addresses.length > 0 ? (
<ComboboxList>
{addresses.map((address, index) => (
<ComboboxOption value={address} key={index} />
))}
</ComboboxList>
<>
<ComboboxList>
{addresses.map((address, index) => (
<ComboboxOption value={address.description} key={index}>
<PlaceIcon className="flex flex-shrink-0 mr4 c-muted-1" />
{renderSuggestionText(address)}
</ComboboxOption>
))}
</ComboboxList>
{renderEngineLogo && (
<div className="flex flex-row-reverse">
<div className="mt3 mb1 mh5">{renderEngineLogo()}</div>
</div>
)}
</>
) : (
<FormattedMessage id="place-components.label.autocompleteAddressFail" />
<div className="flex items-center pv3 ph5">
<div className="flex flex-shrink-0 mr4 c-muted-3">
<IconWarning />
</div>
<div className="truncate c-muted-2 fw6">
<FormattedMessage id="place-components.label.autocompleteAddressFail" />
</div>
</div>
)}
</ComboboxPopover>
</Combobox>
Expand Down
Loading