Skip to content

Commit

Permalink
convert search box and results to functional component
Browse files Browse the repository at this point in the history
  • Loading branch information
zoran995 committed Feb 19, 2025
1 parent 82b2734 commit f4e193d
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 147 deletions.
22 changes: 0 additions & 22 deletions lib/ReactViews/Search/SearchBoxAndResults.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { reaction, runInAction } from "mobx";
import { action, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import PropTypes from "prop-types";
import React from "react";
import { useEffect, useRef, type FC } from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import styled, { useTheme } from "styled-components";
import { addMarker, removeMarker } from "../../Models/LocationMarkerUtils";
import Box from "../../Styled/Box";
import { RawButton } from "../../Styled/Button";
import Icon, { StyledIcon } from "../../Styled/Icon";
import Spacing from "../../Styled/Spacing";
import Text from "../../Styled/Text";
import LocationSearchResults from "../Search/LocationSearchResults";
import SearchBox from "../Search/SearchBox";

export function SearchInDataCatalog({ viewState, handleClick }) {
import { useViewState } from "../Context";
import LocationSearchResults from "./LocationSearchResults";
import SearchBox from "./SearchBox";

export function SearchInDataCatalog({
handleClick
}: {
handleClick: () => void;
}) {
const viewState = useViewState();
const locationSearchText = viewState.searchState.locationSearchText;
const { t } = useTranslation();
return (
Expand Down Expand Up @@ -52,7 +58,7 @@ SearchInDataCatalog.propTypes = {

const PresentationBox = styled(Box).attrs({
fullWidth: true
})`
})<{ highlightBottom: boolean }>`
${(props) =>
props.highlightBottom &&
`
Expand All @@ -67,85 +73,65 @@ const PresentationBox = styled(Box).attrs({

export const LOCATION_SEARCH_INPUT_NAME = "LocationSearchInput";

export class SearchBoxAndResultsRaw extends React.Component {
constructor(props) {
super(props);
this.locationSearchRef = React.createRef();
}
interface SearchBoxAndResultsProps {
placeholder: string;
}

componentDidMount() {
this.props.viewState.updateAppRef(
LOCATION_SEARCH_INPUT_NAME,
this.locationSearchRef
);
this.subscribeToProps();
}
export const SearchBoxAndResults: FC<SearchBoxAndResultsProps> = observer(
({ placeholder }) => {
const locationSearchRef = useRef(null);

componentDidUpdate() {
this.subscribeToProps();
}
const viewState = useViewState();
const theme = useTheme();

componentWillUnmount() {
this.unsubscribeFromProps();
}
useEffect(() => {
viewState.updateAppRef(LOCATION_SEARCH_INPUT_NAME, locationSearchRef);

subscribeToProps() {
this.unsubscribeFromProps();

// TODO(wing): why is this a reaction here and not in viewState itself?
// Close the search results when the Now Viewing changes (so that it's visible).
this._nowViewingChangeSubscription = reaction(
() => this.props.terria.workbench.items,
() => {
this.props.viewState.searchState.showLocationSearchResults = false;
}
);
}
// TODO(wing): why is this a reaction here and not in viewState itself?
// Close the search results when the Now Viewing changes (so that it's visible).
const disposeReaction = reaction(
() => viewState.terria.workbench.items,
() => {
viewState.searchState.showLocationSearchResults = false;
}
);

unsubscribeFromProps() {
if (this._nowViewingChangeSubscription) {
this._nowViewingChangeSubscription();
this._nowViewingChangeSubscription = undefined;
}
}
return disposeReaction;
}, [viewState]);

changeSearchText(newText) {
runInAction(() => {
this.props.viewState.searchState.locationSearchText = newText;
const toggleShowLocationSearchResults = action((bool: boolean) => {
viewState.searchState.showLocationSearchResults = bool;
});

if (newText.length === 0) {
removeMarker(this.props.terria);
const changeSearchText = (newText: string) => {
runInAction(() => {
this.toggleShowLocationSearchResults(false);
viewState.searchState.locationSearchText = newText;
});
}
if (
newText.length > 0 &&
!this.props.viewState.searchState.showLocationSearchResults
) {
runInAction(() => {
this.toggleShowLocationSearchResults(true);
});
}
}

search() {
this.props.viewState.searchState.searchLocations();
}
if (newText.length === 0) {
removeMarker(viewState.terria);
runInAction(() => {
toggleShowLocationSearchResults(false);
});
}
if (
newText.length > 0 &&
!viewState.searchState.showLocationSearchResults
) {
runInAction(() => {
toggleShowLocationSearchResults(true);
});
}
};

toggleShowLocationSearchResults(bool) {
runInAction(() => {
this.props.viewState.searchState.showLocationSearchResults = bool;
});
}
const search = () => {
viewState.searchState.searchLocations();
};

startLocationSearch() {
this.toggleShowLocationSearchResults(true);
}
const startLocationSearch = () => {
toggleShowLocationSearchResults(true);
};

render() {
const { viewState, placeholder } = this.props;
const searchState = viewState.searchState;
const locationSearchText = searchState.locationSearchText;

Expand All @@ -158,10 +144,10 @@ export class SearchBoxAndResultsRaw extends React.Component {
<Box fullWidth>
<PresentationBox highlightBottom={shouldShowResults}>
<SearchBox
ref={this.locationSearchRef}
onSearchTextChanged={this.changeSearchText.bind(this)}
onDoSearch={this.search.bind(this)}
onFocus={this.startLocationSearch.bind(this)}
ref={locationSearchRef}
onSearchTextChanged={changeSearchText.bind(this)}
onDoSearch={search.bind(this)}
onFocus={startLocationSearch.bind(this)}
searchText={searchState.locationSearchText}
placeholder={placeholder}
/>
Expand All @@ -174,22 +160,22 @@ export class SearchBoxAndResultsRaw extends React.Component {
column
css={`
top: 100%;
background-color: ${(props) => props.theme.greyLightest};
background-color: ${theme.greyLightest};
max-height: calc(100vh - 120px);
border-radius: 0 0 ${(props) => props.theme.radius40Button}px
${(props) => props.theme.radius40Button}px;
border-radius: 0 0 ${theme.radius40Button}px
${theme.radius40Button}px;
`}
>
{/* search {searchterm} in data catalog */}
{/* ~TODO: Put this back once we add a MobX DataCatalogSearch Provider~ */}
{/* TODO2: Implement a more generic MobX DataCatalogSearch */}
{this.props.terria.searchBarModel.showSearchInCatalog &&
{viewState.terria.searchBarModel.showSearchInCatalog &&
searchState.catalogSearchProvider && (
<Box column paddedRatio={2}>
<SearchInDataCatalog
viewState={viewState}
handleClick={() => {
this.toggleShowLocationSearchResults(false);
toggleShowLocationSearchResults(false);
}}
/>
</Box>
Expand All @@ -202,14 +188,19 @@ export class SearchBoxAndResultsRaw extends React.Component {
>
{searchState.locationSearchResults.map((search) => (
<LocationSearchResults
theme={theme}
key={search.searchProvider.uniqueId}
terria={this.props.terria}
viewState={this.props.viewState}
terria={viewState.terria}
viewState={viewState}
search={search}
locationSearchText={locationSearchText}
onLocationClick={(result) => {
addMarker(this.props.terria, result);
result.clickAction();
if (!result.location) return;
addMarker(viewState.terria, {
name: result.name,
location: result.location
});
result.clickAction?.();
runInAction(() => {
searchState.showLocationSearchResults = false;
});
Expand All @@ -226,12 +217,8 @@ export class SearchBoxAndResultsRaw extends React.Component {
</Text>
);
}
}
);

SearchBoxAndResultsRaw.propTypes = {
terria: PropTypes.object.isRequired,
viewState: PropTypes.object.isRequired,
placeholder: PropTypes.string.isRequired
};
SearchBoxAndResults.displayName = "SearchBoxAndResults";

export default observer(SearchBoxAndResultsRaw);
export default SearchBoxAndResults;
2 changes: 0 additions & 2 deletions lib/ReactViews/SidePanel/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@ const SidePanel = observer<React.FC<SidePanelProps>>(
`}
>
<SearchBoxAndResults
viewState={viewState}
terria={terria}
placeholder={applyTranslationIfExists(
terria.searchBarModel.placeholder,
i18n
Expand Down
47 changes: 17 additions & 30 deletions test/ReactViews/Search/SearchBoxAndResultsSpec.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { create } from "react-test-renderer";
import React from "react";
import { act } from "react-dom/test-utils";
import { runInAction } from "mobx";
import Terria from "../../../lib/Models/Terria";
import { act } from "react-dom/test-utils";
import { ThemeProvider } from "styled-components";
import CommonStrata from "../../../lib/Models/Definition/CommonStrata";
import CatalogSearchProvider from "../../../lib/Models/SearchProviders/CatalogSearchProvider";
import Terria from "../../../lib/Models/Terria";
import ViewState from "../../../lib/ReactViewModels/ViewState";
import SearchBoxAndResults, {
SearchInDataCatalog
} from "../../../lib/ReactViews/Search/SearchBoxAndResults";
import { ThemeProvider } from "styled-components";
import { terriaTheme } from "../../../lib/ReactViews/StandardUserInterface";
import CatalogSearchProvider from "../../../lib/Models/SearchProviders/CatalogSearchProvider";
import { createWithContexts } from "../withContext";

describe("SearchBoxAndResults", function () {
let terria: Terria;
Expand All @@ -36,13 +35,10 @@ describe("SearchBoxAndResults", function () {
viewState.searchState.locationSearchResults = [];
});
act(() => {
testRenderer = create(
testRenderer = createWithContexts(
viewState,
<ThemeProvider theme={terriaTheme}>
<SearchBoxAndResults
t={() => {}}
terria={terria}
viewState={viewState}
/>
<SearchBoxAndResults placeholder="Search" />
</ThemeProvider>
);
});
Expand All @@ -63,13 +59,10 @@ describe("SearchBoxAndResults", function () {
viewState.searchState.locationSearchResults = [];
});
act(() => {
testRenderer = create(
testRenderer = createWithContexts(
viewState,
<ThemeProvider theme={terriaTheme}>
<SearchBoxAndResults
t={() => {}}
terria={terria}
viewState={viewState}
/>
<SearchBoxAndResults placeholder="Search" />
</ThemeProvider>
);
});
Expand All @@ -89,13 +82,10 @@ describe("SearchBoxAndResults", function () {
viewState.terria.searchBarModel.catalogSearchProvider = undefined;
});
act(() => {
testRenderer = create(
testRenderer = createWithContexts(
viewState,
<ThemeProvider theme={terriaTheme}>
<SearchBoxAndResults
t={() => {}}
terria={terria}
viewState={viewState}
/>
<SearchBoxAndResults placeholder="Search" />
</ThemeProvider>
);
});
Expand All @@ -122,13 +112,10 @@ describe("SearchBoxAndResults", function () {
);
});
act(() => {
testRenderer = create(
testRenderer = createWithContexts(
viewState,
<ThemeProvider theme={terriaTheme}>
<SearchBoxAndResults
t={() => {}}
terria={terria}
viewState={viewState}
/>
<SearchBoxAndResults placeholder="Search" />
</ThemeProvider>
);
});
Expand Down

0 comments on commit f4e193d

Please sign in to comment.