diff --git a/package-lock.json b/package-lock.json index 5ad624875..240ea0ffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6654,14 +6654,6 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, - "node_modules/@sveltejs/kit/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@sveltejs/kit/node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -8399,13 +8391,13 @@ } }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -8413,7 +8405,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -9380,10 +9372,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -10247,13 +10238,14 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -10665,6 +10657,25 @@ "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==" }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -10964,17 +10975,17 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -11416,9 +11427,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -12417,9 +12428,9 @@ } }, "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", "dev": true }, "node_modules/ipaddr.js": { @@ -17671,9 +17682,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", diff --git a/src/config/config.js b/src/config/config.js index 73ff826d5..473668561 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -63,6 +63,7 @@ export const IMAGE_WIDTHS = IMAGE_WIDTHS_ENTRIES.map(([name, width]) => [ export const IMAGE_WIDTHS_MAP = new Map(IMAGE_WIDTHS_ENTRIES); +export const DEFAULT_PER_PAGE = 25; export const MAX_PER_PAGE = 100; export const PDF_SIZE_LIMIT = 525336576; diff --git a/src/lib/api/documents.ts b/src/lib/api/documents.ts index 3dfb4baaa..b6b88f99f 100644 --- a/src/lib/api/documents.ts +++ b/src/lib/api/documents.ts @@ -1,7 +1,7 @@ /** API helpers related to documents. * Lots of duplicated code here that should get consolidated at some point. */ -import type { Document, sizes } from "./types"; +import type { Document, DocumentResults, SearchOptions, sizes } from "./types"; import { error } from "@sveltejs/kit"; @@ -10,17 +10,29 @@ import { isOrg } from "@/api/types/orgAndUser"; import { APP_URL, BASE_API_URL } from "@/config/config.js"; import { isErrorCode } from "../utils"; -/** Search documents */ +/** + * Search documents + * https://www.documentcloud.org/help/search/ + * + * */ export async function search( query = "", - highlight = false, + options: SearchOptions = { + hl: Boolean(query), + per_page: 25, + cursor: "", + version: "2.0", + }, fetch = globalThis.fetch, -) { +): Promise { const endpoint = new URL("documents/search/", BASE_API_URL); endpoint.searchParams.set("expand", DEFAULT_EXPAND); endpoint.searchParams.set("q", query); - endpoint.searchParams.set("hl", String(highlight)); + + for (const [k, v] of Object.entries(options)) { + endpoint.searchParams.set(k, String(v)); + } const resp = await fetch(endpoint, { credentials: "include" }); diff --git a/src/lib/api/types.d.ts b/src/lib/api/types.d.ts index d686d6680..474da49ee 100644 --- a/src/lib/api/types.d.ts +++ b/src/lib/api/types.d.ts @@ -105,7 +105,6 @@ export interface Document { note_highlights?: Record; } -// export type DocumentResults = Page; export interface DocumentResults extends Page {} export interface Note { @@ -135,6 +134,14 @@ export interface Section { export type SectionResults = Page
; +export interface SearchOptions { + hl?: boolean; + per_page?: number; + cursor?: string; + expand?: string; + version?: number | string; +} + export interface OEmbed { version: "1.0"; provider_name: "DocumentCloud"; diff --git a/src/lib/components/Search.svelte b/src/lib/components/Search.svelte index b60959d78..a6e59aa50 100644 --- a/src/lib/components/Search.svelte +++ b/src/lib/components/Search.svelte @@ -1,6 +1,5 @@ -
+ + {/if} + diff --git a/src/lib/components/documents/stories/ResultsList.stories.svelte b/src/lib/components/documents/stories/ResultsList.stories.svelte index 886307168..9e31ad2a5 100644 --- a/src/lib/components/documents/stories/ResultsList.stories.svelte +++ b/src/lib/components/documents/stories/ResultsList.stories.svelte @@ -1,11 +1,13 @@ -
+
-
+
+
+ + +
diff --git a/src/routes/app/+page.svelte b/src/routes/app/+page.svelte index 08702a3fd..fae86fd6c 100644 --- a/src/routes/app/+page.svelte +++ b/src/routes/app/+page.svelte @@ -1,39 +1,27 @@ @@ -44,45 +32,48 @@ {#await searchResults} Loading… {:then results} - + {/await} - + + diff --git a/src/routes/app/+page.ts b/src/routes/app/+page.ts index 2798376c6..29764d20c 100644 --- a/src/routes/app/+page.ts +++ b/src/routes/app/+page.ts @@ -1,11 +1,32 @@ -import { search } from "$lib/api/documents.js"; +import type { SearchOptions } from "$lib/api/types"; + +import { DEFAULT_PER_PAGE } from "@/config/config.js"; +import { search } from "$lib/api/documents"; export async function load({ url, fetch }) { const query = url.searchParams.get("q") || ""; - const searchResults = search(query, true, fetch); + const per_page = +url.searchParams.get("per_page") || DEFAULT_PER_PAGE; + const cursor = url.searchParams.get("cursor") || ""; + + const options: SearchOptions = { + hl: Boolean(query), + version: "2.0", + }; + + if (per_page) { + options.per_page = per_page; + } + + if (cursor) { + options.cursor = cursor; + } + + const searchResults = search(query, options, fetch); return { - searchResults, query, + per_page, + cursor, + searchResults, }; } diff --git a/src/style/kit.css b/src/style/kit.css index d0b920685..1965f00cd 100644 --- a/src/style/kit.css +++ b/src/style/kit.css @@ -139,3 +139,21 @@ dd { font-size: var(--font-s, 0.875rem); font-weight: var(--font-regular, 400); } + +/* +Utility classes +*/ + +/* https://css-tricks.com/inclusively-hidden/ + * Hiding class, making content visible only to screen readers but not visually + * "sr" meaning "screen-reader" + */ +.sr-only:not(:focus):not(:active) { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +}