• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

terrestris / react-geo / 18933910017

30 Oct 2025 08:04AM UTC coverage: 67.733%. Remained the same
18933910017

push

github

web-flow
Merge pull request #4435 from terrestris/dependabot/npm_and_yarn/babel/preset-typescript-7.28.5

build(deps-dev): bump @babel/preset-typescript from 7.27.1 to 7.28.5

672 of 1082 branches covered (62.11%)

Branch coverage included in aggregate %.

1234 of 1732 relevant lines covered (71.25%)

13.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.17
/src/Field/SearchField/SearchField.tsx
1
import './SearchField.less';
2

3
import React, { ReactElement, useCallback, useEffect, useMemo, useState } from 'react';
4

5
import { AutoComplete, Spin } from 'antd';
6

7
import { AutoCompleteProps } from 'antd/lib/auto-complete';
8
import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
9
import { Extent } from 'ol/extent';
10
import OlFormatGeoJSON from 'ol/format/GeoJSON';
11
import { transformExtent } from 'ol/proj';
12

13
import { SearchFunction, SearchOptions, useSearch } from '@terrestris/react-util';
14
import useMap from '@terrestris/react-util/dist/Hooks/useMap/useMap';
15

16
import { CSS_PREFIX } from '../../constants';
17

18
export type SearchProps<
19
  G extends Geometry = Geometry,
20
  T extends NonNullable<GeoJsonProperties> = Record<string, any>,
21
  C extends FeatureCollection<G, T> = FeatureCollection<G, T>
22
> = {
23
  searchFunction: SearchFunction<G, T, C>;
24
  searchOptions?: SearchOptions<G, T, C>;
25
  getValue?: (feature: Feature<G, T>) => string;
26
  /**
27
   * An onSelect function which gets called with the selected item as it is
28
   * returned by the search function.
29
   */
30
  onSelect?: (feature: Feature<G, T>) => void;
31
  /**
32
   * A function which gets called with the search results if the search is completed.
33
   */
34
  onSearchCompleted?: (featureCollection: FeatureCollection<G, T> | undefined) => void;
35
  /**
36
   * An optional CSS class which should be added.
37
   */
38
  className?: string;
39
  /**
40
   * A function that gets called when the clear Button is pressed or the input
41
   * value is empty.
42
   */
43
  onClear?: () => void;
44
  zoomToFeature?: boolean;
45
  getExtent?: (feature: Feature<G, T>) => Extent;
46
  autoCompleteDisabled?: boolean;
47
} & Omit<AutoCompleteProps, 'onSelect'|'onSearch'|'onChange'|'onClear'|'notFoundContent'>;
48

49
/**
50
 * The SearchField.
51
 */
52
export function SearchField<
53
  G extends Geometry = Geometry,
54
  T extends NonNullable<GeoJsonProperties> = Record<string, any>,
55
  C extends FeatureCollection<G, T> = FeatureCollection<G, T>
56
>({
57
  className = `${CSS_PREFIX}search`,
4✔
58
  onSelect,
59
  onSearchCompleted,
60
  getValue = () => '',
✔
61
  searchFunction,
62
  searchOptions = {},
8✔
63
  zoomToFeature = true,
8✔
64
  autoCompleteDisabled = false,
5✔
65
  getExtent,
66
  ...passThroughProps
67
}: SearchProps<G, T, C>): ReactElement {
68

69
  const [searchTerm, setSearchTerm] = useState<string>('');
8✔
70
  const map = useMap();
8✔
71

72
  const {
73
    featureCollection,
74
    loading
75
  } = useSearch<G, T, C>(searchFunction, searchTerm, searchOptions);
8✔
76

77
  useEffect(() => {
8✔
78
    if (onSearchCompleted) {
4✔
79
      onSearchCompleted(featureCollection);
2✔
80
    }
81
  }, [featureCollection, onSearchCompleted]);
82

83
  const options = useMemo(
8✔
84
    () => featureCollection?.features.map(f => ({
6✔
85
      label: getValue(f),
86
      value: getValue(f)
87
    })),
88
    [featureCollection, getValue]
89
  );
90

91
  const onMenuItemSelected = useCallback((value: string) => {
8✔
92
    const selected = featureCollection?.features.find(f => getValue(f) === value);
×
93
    if (selected && onSelect) {
×
94
      onSelect(selected);
×
95
    }
96
    if (selected && zoomToFeature) {
×
97
      if (!map) {
×
98
        return;
×
99
      }
100
      let extent: Extent;
101
      if (getExtent) {
×
102
        extent = getExtent(selected);
×
103
      } else {
104
        const olFormat = new OlFormatGeoJSON();
×
105
        const geometry = olFormat.readGeometry(selected.geometry);
×
106
        extent = geometry.getExtent();
×
107
      }
108

109
      const olView = map?.getView();
×
110

111
      extent = transformExtent(extent, 'EPSG:4326', olView.getProjection());
×
112

113
      olView.fit(extent, {
×
114
        duration: 500
115
      });
116
    }
117
  }, [map, onSelect, getValue, getExtent, featureCollection?.features, zoomToFeature]);
118

119
  return (
8✔
120
    <AutoComplete
121
      className={className}
122
      allowClear
123
      classNames={{
124
        popup: {
125
          root: autoCompleteDisabled ? 'autocomplete-disabled' : undefined
8✔
126
        }
127
      }}
128
      popupRender={autoCompleteDisabled ? () => <></> : undefined}
4✔
129
      onSearch={text =>
130
        setSearchTerm(text)
2✔
131
      }
132
      onClear={() =>
133
        setSearchTerm('')
×
134
      }
135
      onSelect={onMenuItemSelected}
136
      options={options}
137
      notFoundContent={loading ? <Spin size="small" /> : null}
8✔
138
      {...passThroughProps}
139
    />
140
  );
141
}
142

143
export default SearchField;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc