From c8e69e3bfdbb2453e6fc1e4b45b6fab756427d1f Mon Sep 17 00:00:00 2001 From: Atul Varma Date: Sat, 12 Oct 2019 18:47:10 -0400 Subject: [PATCH] Use @justfixnyc/geosearch-requester. --- jest.config.js | 5 +- package.json | 4 +- src/components/geo-autocomplete.tsx | 4 +- src/util/geo-autocomplete-base.ts | 203 ---------------------------- yarn.lock | 5 + 5 files changed, 15 insertions(+), 206 deletions(-) delete mode 100644 src/util/geo-autocomplete-base.ts diff --git a/jest.config.js b/jest.config.js index 491b7ad9f..823897167 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,8 +3,11 @@ module.exports = { "/src" ], "transform": { - "^.+\\.tsx?$": "/jest.custom-transformer.js" + "^.+\\.[jt]sx?$": "/jest.custom-transformer.js" }, + "transformIgnorePatterns": [ + "/node_modules/(?!@justfixnyc).+\\.js$" + ], moduleNameMapper: { ".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`, ".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `/__mocks__/file-mock.js`, diff --git a/package.json b/package.json index 5263fc431..460b449f9 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@babel/core": "^7.6.4", "@contentful/rich-text-react-renderer": "^13.4.0", + "@justfixnyc/geosearch-requester": "^0.0.6", "@types/classnames": "^2.2.9", "@types/react-helmet": "^5.0.7", "bulma": "^0.7.5", @@ -41,7 +42,8 @@ "read-more-react": "^1.0.9", "tslint": "^5.11.0", "typescript": "^3.1.1", - "webpack": "^4.0.0" + "webpack": "^4.0.0", + "whatwg-fetch": "2.0.4" }, "keywords": [ "gatsby" diff --git a/src/components/geo-autocomplete.tsx b/src/components/geo-autocomplete.tsx index 8f464329a..941c24c93 100644 --- a/src/components/geo-autocomplete.tsx +++ b/src/components/geo-autocomplete.tsx @@ -1,5 +1,7 @@ +import 'whatwg-fetch'; // Needed for IE11! + import React from 'react'; -import { GeoSearchBoroughGid, GeoSearchRequester, GeoSearchResults } from "../util/geo-autocomplete-base"; +import { GeoSearchBoroughGid, GeoSearchRequester, GeoSearchResults } from "@justfixnyc/geosearch-requester"; import Downshift, { DownshiftInterface, ControllerStateAndHelpers } from 'downshift'; import classnames from 'classnames'; import { BoroughChoice, getBoroughChoiceLabels } from '../util/borough-choices'; diff --git a/src/util/geo-autocomplete-base.ts b/src/util/geo-autocomplete-base.ts deleted file mode 100644 index 8231e6d9a..000000000 --- a/src/util/geo-autocomplete-base.ts +++ /dev/null @@ -1,203 +0,0 @@ -/** - * For documentation about this endpoint, see: - * - * https://geosearch.planninglabs.nyc/docs/#autocomplete - */ -const GEO_AUTOCOMPLETE_URL = 'https://geosearch.planninglabs.nyc/v1/autocomplete'; - -/** - * The keys here were obtained experimentally, I'm not actually sure - * if/where they are formally specified. - */ -export enum GeoSearchBoroughGid { - Manhattan = 'whosonfirst:borough:1', - Bronx = 'whosonfirst:borough:2', - Brooklyn = 'whosonfirst:borough:3', - Queens = 'whosonfirst:borough:4', - StatenIsland = 'whosonfirst:borough:5', -} - -/** - * This is what the NYC Geosearch API returns from its - * autocomplete endpoint. - * - * Note that some of the fields are "unknown", which - * just implies that they exist but we're not really - * sure what type they are (nor do we particularly - * care, at the moment, for our purposes). - */ -export interface GeoSearchResults { - bbox: unknown; - features: GeoSearchFeature[]; -} - -export interface GeoSearchFeature { - geometry: unknown; - properties: GeoSearchProperties -} - -/** - * Note that these are by no means all the - * properties, they're just the ones we care about. - */ -export interface GeoSearchProperties { - /** e.g. "Brooklyn" */ - borough: string; - - /** e.g. "whosonfirst:borough:2" */ - borough_gid: GeoSearchBoroughGid; - - /** e.g. "150" */ - housenumber: string; - - /** e.g. "150 COURT STREET" */ - name: string; - - /** e.g. "150 COURT STREET, Brooklyn, New York, NY, USA" */ - label: string; -} - -/** - * Options for the requester constructor. - */ -export interface GeoSearchRequesterOptions { - /** - * A factory that returns an AbortController [1] instance, - * or undefined if the platform doesn't support aborting - * fetch requests. - * - * [1] https://developer.mozilla.org/en-US/docs/Web/API/AbortController - */ - createAbortController: () => AbortController|undefined; - - /** - * A reference to the platform's Fetch API [1]. Note that - * this will always be called with "this" bound to the - * global scope. - * - * [1] https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API - */ - fetch: typeof window.fetch, - - /** - * The number of milliseconds to wait before we actually issue - * a search request. This is primarily intended to allow - * keyboard-based autocomplete UIs to not spam the server - * when the user is typing quickly. - */ - throttleMs: number, - - /** - * A callback that's called whenever an error occurs fetching - * autocomplete results. - */ - onError: (e: Error) => void; - - /** - * A callback that's called whenever results are fetched for - * the most recently issued query. This will never be - * called for stale queries. - */ - onResults: (results: GeoSearchResults) => void; -} - -/** - * This class can be used to issue search requests - * based on a query whose value may change over time - * due to e.g. keyboard input. - */ -export class GeoSearchRequester { - private requestId: number; - private abortController?: AbortController; - private throttleTimeout: number|null; - - constructor(readonly options: GeoSearchRequesterOptions) { - this.requestId = 0; - this.abortController = options.createAbortController(); - this.throttleTimeout = null; - } - - /** - * Fetch results for the given query, returning null if the - * network request was aborted. - */ - private fetchResults(value: string): Promise { - const url = `${GEO_AUTOCOMPLETE_URL}?text=${encodeURIComponent(value)}`; - - // It's important that we pull fetch out as its own variable, - // as this will bind its "this" context to the global scope - // when it's called, which is important for most/all window.fetch() - // implementations. - const { fetch } = this.options; - - return fetch(url, { - signal: this.abortController && this.abortController.signal - }).then(res => { - if (res.status !== 200) { - throw new Error(`Received HTTP ${res.status}`); - } - return res.json(); - }).catch((e) => { - if (e instanceof DOMException && e.name === 'AbortError') { - // Don't worry about it, the user just aborted the request. - return null; - } else { - throw e; - } - }); - } - - /** - * Fetch results for the given query, returning null if the - * query was superseded by a newer one. - */ - private async fetchResultsForLatestRequest(value: string): Promise { - const originalRequestId = this.requestId; - let results = await this.fetchResults(value); - if (this.requestId === originalRequestId) { - return results; - } - return null; - } - - /** - * Abort any currently in-flight requests. - */ - private resetSearchRequest() { - if (this.throttleTimeout !== null) { - window.clearTimeout(this.throttleTimeout); - this.throttleTimeout = null; - } - this.requestId++; - if (this.abortController) { - this.abortController.abort(); - this.abortController = this.options.createAbortController(); - } - } - - /** - * Change the current search request to a new query. Return - * whether the new query is non-empty. - */ - changeSearchRequest(value: string): boolean { - this.resetSearchRequest(); - if (value.length > 0) { - this.throttleTimeout = window.setTimeout(() => { - this.fetchResultsForLatestRequest(value).catch(this.options.onError).then(results => { - if (results) { - this.options.onResults(results); - } - }); - }, this.options.throttleMs); - return true; - } - return false; - } - - /** - * Clean up all resources used by the requester. - */ - shutdown() { - this.resetSearchRequest(); - } -} diff --git a/yarn.lock b/yarn.lock index 1f450fdb5..ca9428611 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1606,6 +1606,11 @@ dependencies: core-js "^2.5.7" +"@justfixnyc/geosearch-requester@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@justfixnyc/geosearch-requester/-/geosearch-requester-0.0.6.tgz#af3144a8b599e45ad3ce5c2190fc60da81106475" + integrity sha512-wHsY9DbiQUp7H/KiQ4+ejTShR6lY3iagiFhw4VoP1kljyem+WIwEkiZNnvUHkOAQIkTtPulLaiKHEMpzaw9xZQ== + "@mikaelkristiansson/domready@^1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@mikaelkristiansson/domready/-/domready-1.0.9.tgz#b2b85d8ac7bb2797e577050b61aeaf1b26fbd906"