Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
function LocationSearch({ outerClassName }) {
// should be the location object from suggestedLocations
const [selectedLocation, setSelectedLocation] = useState("");
const suggestedLocations = useSelector(
state => state.Weather.suggestedLocations
);
const locationSearchString = useSelector(
state => state.Weather.locationSearchString
);
const [userInput, setUserInput] = useState(locationSearchString || "");
const inputRef = useRef(null);
const dispatch = useDispatch();
useEffect(() => {
if (selectedLocation) {
dispatch(
ac.AlsoToMain({
type: at.WEATHER_LOCATION_DATA_UPDATE,
data: {
city: selectedLocation.localized_name,
adminName: selectedLocation.administrative_area,
country: selectedLocation.country,
},
})
);
dispatch(ac.SetPref("weather.query", selectedLocation.key));
dispatch(
ac.BroadcastToContent({
type: at.WEATHER_SEARCH_ACTIVE,
data: false,
})
);
}
}, [selectedLocation, dispatch]);
// when component mounts, set focus to input
useEffect(() => {
inputRef?.current?.focus();
}, [inputRef]);
function handleChange(event) {
const { value } = event.target;
setUserInput(value);
// if the user input contains less than three characters and suggestedLocations is not an empty array,
// reset suggestedLocations to [] so there arent incorrect items in the datalist
if (value.length < 3 && suggestedLocations.length) {
dispatch(
ac.AlsoToMain({
type: at.WEATHER_LOCATION_SUGGESTIONS_UPDATE,
data: [],
})
);
}
// find match in suggestedLocation array
const match = suggestedLocations?.find(({ key }) => key === value);
if (match) {
setSelectedLocation(match);
setUserInput(
`${match.localized_name}, ${match.administrative_area.localized_name}`
);
} else if (value.length >= 3 && !match) {
dispatch(
ac.AlsoToMain({
type: at.WEATHER_LOCATION_SEARCH_UPDATE,
data: value,
})
);
}
}
function handleCloseSearch() {
dispatch(
ac.BroadcastToContent({
type: at.WEATHER_SEARCH_ACTIVE,
data: false,
})
);
setUserInput("");
}
function handleKeyDown(e) {
if (e.key === "Escape") {
handleCloseSearch();
}
}
return (
<div className={`${outerClassName} location-search`}>
<div className="location-input-wrapper">
<div className="search-icon" />
<input
ref={inputRef}
list="merino-location-list"
type="text"
data-l10n-id="newtab-weather-change-location-search-input-placeholder"
onChange={handleChange}
value={userInput}
onKeyDown={handleKeyDown}
/>
<moz-button
class="close-icon"
type="icon ghost"
size="small"
iconSrc="chrome://global/skin/icons/close.svg"
onClick={handleCloseSearch}
/>
<datalist id="merino-location-list">
{(suggestedLocations || []).map(merinoLcation => (
<option value={merinoLcation.key} key={merinoLcation.key}>
{merinoLcation.localized_name},{" "}
{merinoLcation.administrative_area.localized_name}
</option>
))}
</datalist>
</div>
</div>
);
}
export { LocationSearch };