From 3239e3a6ce13a94feed754c43d00de9bf1128ca8 Mon Sep 17 00:00:00 2001 From: Alex Garel Date: Wed, 10 Apr 2024 11:12:44 +0200 Subject: [PATCH 1/6] =?UTF-8?q?feat:=C2=A0wip=20on=20search=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 10 +- confs/nginx.conf | 2 +- docker/dev.yml | 3 + frontend/package-lock.json | 787 ++++++++++ frontend/package.json | 1 + frontend/src/off.html | 1651 ++++++++++++++++++++ frontend/src/search-a-licious.ts | 69 +- frontend/src/search-bar.ts | 60 + frontend/src/search-ctl.ts | 103 ++ frontend/src/test/search-a-licious_test.ts | 68 - frontend/src/test/search-bar_test.ts | 28 + frontend/web-dev-server.config.js | 8 +- 12 files changed, 2645 insertions(+), 145 deletions(-) create mode 100644 frontend/src/off.html create mode 100644 frontend/src/search-bar.ts create mode 100644 frontend/src/search-ctl.ts delete mode 100644 frontend/src/test/search-a-licious_test.ts create mode 100644 frontend/src/test/search-bar_test.ts diff --git a/Makefile b/Makefile index 3fe27199..76efea58 100644 --- a/Makefile +++ b/Makefile @@ -91,9 +91,15 @@ check: # note: this is called by pre-commit check_front: _ensure_network ${DOCKER_COMPOSE} run --rm -T search_nodejs npm run check -lint: - @echo "🔎 Running linters..." + +lint: lint_back lint_front + +lint_back: + @echo "🔎 Running linters for backend code..." pre-commit run black --all-files + +lint_front: + @echo "🔎 Running linters for frontend code..." ${DOCKER_COMPOSE} run --rm search_nodejs npm run format #-------# diff --git a/confs/nginx.conf b/confs/nginx.conf index 590f90ae..6ecb67c8 100644 --- a/confs/nginx.conf +++ b/confs/nginx.conf @@ -35,7 +35,7 @@ server { set $search_nodejs search_nodejs; # rewrite to get rid of /static # double $ to avoid being interpreted by interpolation - rewrite ^/static${DEV_UI_SUFFIX}/(.*)$ /$1 break; + rewrite ^/static${DEV_UI_SUFFIX}/(.*)$ /public/$1 break; # important do not add a / at the end for node is picky on not having '//' in url proxy_pass http://$search_nodejs:8000; } diff --git a/docker/dev.yml b/docker/dev.yml index 0aa053ca..e1c63faf 100644 --- a/docker/dev.yml +++ b/docker/dev.yml @@ -42,6 +42,9 @@ services: # by setting PROD_UI_SUFFIX to "" and DEV_UI_SUFFIX to "-dev" PROD_UI_SUFFIX: "${PROD_UI_SUFFIX--static}" DEV_UI_SUFFIX: "${DEV_UI_SUFFIX-}" + volumes: + # dynamic mount + - ./frontend/public:/opt/search-a-licious/public # Node that create the webcomponents diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 304ebbd8..4d34c186 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,7 @@ "@typescript-eslint/parser": "^5.25.0", "@web/dev-server": "^0.1.31", "@web/dev-server-legacy": "^1.0.0", + "@web/rollup-plugin-html": "^2.3.0", "@web/test-runner": "^0.15.0", "@web/test-runner-playwright": "^0.9.0", "@webcomponents/webcomponentsjs": "^2.8.0", @@ -2037,6 +2038,79 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -2207,6 +2281,16 @@ "lit-html": "^2.0.0 || ^3.0.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@puppeteer/browsers": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-0.5.0.tgz", @@ -3077,6 +3161,36 @@ "node": ">=12.0.0" } }, + "node_modules/@web/rollup-plugin-html": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@web/rollup-plugin-html/-/rollup-plugin-html-2.3.0.tgz", + "integrity": "sha512-ap4AisBacK6WwrTnVlPErupxlywWU1ELsjGIMZ4VpofvhbVTBIGErJo5VEj2mSJyEH3I1EbzUcWuhDCePrnWEw==", + "dev": true, + "dependencies": { + "@web/parse5-utils": "^2.1.0", + "glob": "^10.0.0", + "html-minifier-terser": "^7.1.0", + "lightningcss": "^1.24.0", + "parse5": "^6.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@web/rollup-plugin-html/node_modules/@web/parse5-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@web/parse5-utils/-/parse5-utils-2.1.0.tgz", + "integrity": "sha512-GzfK5disEJ6wEjoPwx8AVNwUe9gYIiwc+x//QYxYDAFKUp4Xb1OJAGLc2l2gVrSQmtPGLKrTRcW90Hv4pEq1qA==", + "dev": true, + "dependencies": { + "@types/parse5": "^6.0.1", + "parse5": "^6.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@web/test-runner": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.15.3.tgz", @@ -3904,6 +4018,22 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camel-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -4171,6 +4301,27 @@ "node": ">= 0.4" } }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -4400,6 +4551,15 @@ "node": ">=12.17" } }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/comment-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.4.tgz", @@ -4667,6 +4827,18 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1107588", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz", @@ -4720,6 +4892,22 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -4732,6 +4920,12 @@ "integrity": "sha512-m953zv0w5oDagTItWm6Auhmk/pY7EiejaqiVbnzSS3HIjh1FCUeK7WzuaVtWPNs58A+/xpIE+/dVk6pKsrua8g==", "dev": true }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4744,6 +4938,12 @@ "integrity": "sha512-xtjfBXn53RORwkbyKvDfTajtnTp0OJoPOIBzXvkNbb7+YYvCHJflba3L7Txyx/6Fov3ov2bGPr/n5MTixmPhdQ==", "dev": true }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -4762,6 +4962,18 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/errorstacks": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/errorstacks/-/errorstacks-2.4.1.tgz", @@ -5421,6 +5633,22 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -5535,6 +5763,28 @@ "node": ">=0.10.0" } }, + "node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -5553,6 +5803,30 @@ "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==", "dev": true }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -5762,6 +6036,27 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, "node_modules/http-assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", @@ -6235,6 +6530,24 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -6514,6 +6827,213 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/lightningcss": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.24.1.tgz", + "integrity": "sha512-kUpHOLiH5GB0ERSv4pxqlL0RYKnOXtgGtVe7shDGfhS0AZ4D1ouKFYAcLcZhql8aMspDNzaUCumGHZ78tb2fTg==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.24.1", + "lightningcss-darwin-x64": "1.24.1", + "lightningcss-freebsd-x64": "1.24.1", + "lightningcss-linux-arm-gnueabihf": "1.24.1", + "lightningcss-linux-arm64-gnu": "1.24.1", + "lightningcss-linux-arm64-musl": "1.24.1", + "lightningcss-linux-x64-gnu": "1.24.1", + "lightningcss-linux-x64-musl": "1.24.1", + "lightningcss-win32-x64-msvc": "1.24.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.24.1.tgz", + "integrity": "sha512-1jQ12jBy+AE/73uGQWGSafK5GoWgmSiIQOGhSEXiFJSZxzV+OXIx+a9h2EYHxdJfX864M+2TAxWPWb0Vv+8y4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.24.1.tgz", + "integrity": "sha512-R4R1d7VVdq2mG4igMU+Di8GPf0b64ZLnYVkubYnGG0Qxq1KaXQtAzcLI43EkpnoWvB/kUg8JKCWH4S13NfiLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.24.1.tgz", + "integrity": "sha512-z6NberUUw5ALES6Ixn2shmjRRrM1cmEn1ZQPiM5IrZ6xHHL5a1lPin9pRv+w6eWfcrEo+qGG6R9XfJrpuY3e4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.24.1.tgz", + "integrity": "sha512-NLQLnBQW/0sSg74qLNI8F8QKQXkNg4/ukSTa+XhtkO7v3BnK19TS1MfCbDHt+TTdSgNEBv0tubRuapcKho2EWw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.24.1.tgz", + "integrity": "sha512-AQxWU8c9E9JAjAi4Qw9CvX2tDIPjgzCTrZCSXKELfs4mCwzxRkHh2RCxX8sFK19RyJoJAjA/Kw8+LMNRHS5qEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.24.1.tgz", + "integrity": "sha512-JCgH/SrNrhqsguUA0uJUM1PvN5+dVuzPIlXcoWDHSv2OU/BWlj2dUYr3XNzEw748SmNZPfl2NjQrAdzaPOn1lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.24.1.tgz", + "integrity": "sha512-TYdEsC63bHV0h47aNRGN3RiK7aIeco3/keN4NkoSQ5T8xk09KHuBdySltWAvKLgT8JvR+ayzq8ZHnL1wKWY0rw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.24.1.tgz", + "integrity": "sha512-HLfzVik3RToot6pQ2Rgc3JhfZkGi01hFetHt40HrUMoeKitLoqUUT5owM6yTZPTytTUW9ukLBJ1pc3XNMSvlLw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.24.1.tgz", + "integrity": "sha512-joEupPjYJ7PjZtDsS5lzALtlAudAbgIBMGJPNeFe5HfdmJXFd13ECmEM+5rXNxYVMRHua2w8132R6ab5Z6K9Ow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lit": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.2.tgz", @@ -6818,6 +7338,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lower-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6972,6 +7507,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -7113,6 +7657,22 @@ "node": ">= 0.6" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/no-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -7369,6 +7929,22 @@ "node": ">=6" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7396,6 +7972,22 @@ "node": ">= 0.8" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -7444,6 +8036,31 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -7902,6 +8519,15 @@ "jsesc": "bin/jsesc" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/repeat-element": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", @@ -8326,6 +8952,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8645,6 +9283,71 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8657,6 +9360,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -9559,6 +10275,77 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 24af45bd..77b9f7e7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -52,6 +52,7 @@ "@typescript-eslint/parser": "^5.25.0", "@web/dev-server": "^0.1.31", "@web/dev-server-legacy": "^1.0.0", + "@web/rollup-plugin-html": "^2.3.0", "@web/test-runner": "^0.15.0", "@web/test-runner-playwright": "^0.9.0", "@webcomponents/webcomponentsjs": "^2.8.0", diff --git a/frontend/src/off.html b/frontend/src/off.html new file mode 100644 index 00000000..e0da1fc1 --- /dev/null +++ b/frontend/src/off.html @@ -0,0 +1,1651 @@ + + + + + Search-a-licious - Open Food Facts + + + + + +
+
+
+
+ +
+
+ + + +
+
+ +
+
+
+
+
+
+ + search + 565 products, found in 1804 ms + +
+ +
+
+
+
+
+ nova_groups +
    +
  • 4 (56)
  • + +
  • 1 (33)
  • + +
  • 3 (18)
  • + +
  • 2 (12)
  • +
+ + owner +
    +
  • org-carrefour (73)
  • + +
  • org-alnatura (1)
  • + +
  • org-carrefour-espana (1)
  • +
+ + categories_tags +
    +
  • en:tests (109)
  • + +
  • en:non-food-products (108)
  • + +
  • en:plant-based-foods-and-beverages (74)
  • + +
  • en:plant-based-foods (64)
  • + +
  • en:cereals-and-potatoes (32)
  • + +
  • en:cereals-and-their-products (29)
  • + +
  • en:dairies (29)
  • + +
  • en:desserts (18)
  • + +
  • en:fresh-foods (17)
  • + +
  • en:canned-foods (16)
  • +
+ + brands_tags +
    +
  • carrefour (77)
  • + +
  • groupe-carrefour (24)
  • + +
  • cmi-carrefour-marchandises-internationales (17)
  • + +
  • test (14)
  • + +
  • la-laitiere (9)
  • + +
  • null (4)
  • + +
  • adelie (3)
  • + +
  • auchan (3)
  • + +
  • dm-bio (3)
  • + +
  • funky-veggie (3)
  • +
+ + ecoscore_grade +
    +
  • unknown (437)
  • + +
  • b (46)
  • + +
  • a (25)
  • + +
  • c (24)
  • + +
  • d (19)
  • + +
  • e (10)
  • + +
  • not-applicable (4)
  • +
+ + states_tags +
    +
  • en:product-name-completed (562)
  • + +
  • en:to-be-completed (545)
  • + +
  • en:characteristics-to-be-completed (520)
  • + +
  • en:origins-to-be-completed (514)
  • + +
  • en:photos-uploaded (505)
  • + +
  • en:packaging-code-to-be-completed (482)
  • + +
  • en:front-photo-selected (479)
  • + +
  • en:photos-to-be-validated (466)
  • + +
  • en:expiration-date-to-be-completed (464)
  • + +
  • en:packaging-photo-to-be-selected (464)
  • +
+ + nutrition_grades +
    +
  • unknown (310)
  • + +
  • not-applicable (113)
  • + +
  • a (37)
  • + +
  • b (32)
  • + +
  • c (28)
  • + +
  • d (26)
  • + +
  • e (19)
  • +
+ + lang +
    +
  • fr (416)
  • + +
  • en (101)
  • + +
  • de (37)
  • + +
  • es (4)
  • + +
  • nl (2)
  • + +
  • af (1)
  • + +
  • ar (1)
  • + +
  • it (1)
  • + +
  • ja (1)
  • + +
  • zh (1)
  • +
+ + countries_tags +
    +
  • en:france (415)
  • + +
  • en:germany (47)
  • + +
  • en:spain (31)
  • + +
  • en:italy (27)
  • + +
  • en:united-states (27)
  • + +
  • en:belgium (21)
  • + +
  • en:poland (14)
  • + +
  • en:switzerland (14)
  • + +
  • en:united-kingdom (12)
  • + +
  • en:romania (11)
  • +
+ + labels_tags +
    +
  • en:distributor-labels (79)
  • + +
  • fr:teste-par-le-panel-test-carrefour (79)
  • + +
  • en:green-dot (58)
  • + +
  • en:nutriscore (52)
  • + +
  • en:made-in-france (50)
  • + +
  • de:öko-test (37)
  • + +
  • en:organic (31)
  • + +
  • de:öko-test-sehr-gut (26)
  • + +
  • en:carrefour-quality (26)
  • + +
  • en:vegetarian (23)
  • +
+
+
+
+
+
+ +
+
+
+ +
    +
  • + Pages: +
  • + +
  • + 1 +
  • + +
  • + 2 +
  • + +
  • + 3 +
  • + +
  • + 4 +
  • + +
  • + 5 +
  • + +
  • + 6 +
  • + +
  • + 7 +
  • + +
  • + 8 +
  • + +
  • + 9 +
  • + +
  • + 10 +
  • + +
  • + Next +
  • + +
  • (24 products per page)
  • +
+ +

Elasticsearch query

+
{
+    "query": {
+        "bool": {
+            "should": [
+                {
+                    "bool": {
+                        "should": [
+                            {
+                                "match_phrase": {
+                                    "product_name.en": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            },
+                            {
+                                "match_phrase": {
+                                    "product_name.fr": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                {
+                    "bool": {
+                        "should": [
+                            {
+                                "match_phrase": {
+                                    "generic_name.en": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            },
+                            {
+                                "match_phrase": {
+                                    "generic_name.fr": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                {
+                    "bool": {
+                        "should": [
+                            {
+                                "match_phrase": {
+                                    "categories.en": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            },
+                            {
+                                "match_phrase": {
+                                    "categories.fr": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                {
+                    "bool": {
+                        "should": [
+                            {
+                                "match_phrase": {
+                                    "labels.en": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            },
+                            {
+                                "match_phrase": {
+                                    "labels.fr": {
+                                        "query": "test",
+                                        "boost": 2.0
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                },
+                {
+                    "match_phrase": {
+                        "brands": {
+                            "query": "test",
+                            "boost": 2.0
+                        }
+                    }
+                },
+                {
+                    "multi_match": {
+                        "query": "test",
+                        "fields": [
+                            "product_name.en",
+                            "product_name.fr",
+                            "generic_name.en",
+                            "generic_name.fr",
+                            "categories.en",
+                            "categories.fr",
+                            "labels.en",
+                            "labels.fr",
+                            "brands"
+                        ]
+                    }
+                }
+            ]
+        }
+    },
+    "aggs": {
+        "brands_tags": {
+            "terms": {
+                "field": "brands_tags"
+            }
+        },
+        "lang": {
+            "terms": {
+                "field": "lang"
+            }
+        },
+        "owner": {
+            "terms": {
+                "field": "owner"
+            }
+        },
+        "categories_tags": {
+            "terms": {
+                "field": "categories_tags"
+            }
+        },
+        "labels_tags": {
+            "terms": {
+                "field": "labels_tags"
+            }
+        },
+        "countries_tags": {
+            "terms": {
+                "field": "countries_tags"
+            }
+        },
+        "states_tags": {
+            "terms": {
+                "field": "states_tags"
+            }
+        },
+        "nutrition_grades": {
+            "terms": {
+                "field": "nutrition_grades"
+            }
+        },
+        "ecoscore_grade": {
+            "terms": {
+                "field": "ecoscore_grade"
+            }
+        },
+        "nova_groups": {
+            "terms": {
+                "field": "nova_groups"
+            }
+        }
+    },
+    "size": 24,
+    "from": 0
+}
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/frontend/src/search-a-licious.ts b/frontend/src/search-a-licious.ts index 792ad197..d5558957 100644 --- a/frontend/src/search-a-licious.ts +++ b/frontend/src/search-a-licious.ts @@ -1,68 +1 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -import {LitElement, html, css} from 'lit'; -import {customElement, property} from 'lit/decorators.js'; - -/** - * An example element. - * - * @fires count-changed - Indicates when the count changes - * @slot - This element has a slot - * @csspart button - The button - */ -@customElement('search-a-licious') -export class SearchALicious extends LitElement { - static override styles = css` - :host { - display: block; - border: solid 1px gray; - padding: 16px; - max-width: 800px; - } - `; - - /** - * The name to say "Hello" to. - */ - @property() - name = 'World'; - - /** - * The number of times the button has been clicked. - */ - @property({type: Number}) - count = 0; - - override render() { - return html` -

${this.sayHello(this.name)}!

- - - `; - } - - private _onClick() { - this.count++; - this.dispatchEvent(new CustomEvent('count-changed')); - } - - /** - * Formats a greeting - * @param name The name to say "Hello" to - */ - sayHello(name: string): string { - return `Hello, ${name}`; - } -} - -declare global { - interface HTMLElementTagNameMap { - 'search-a-licious': SearchALicious; - } -} +// import {SearchaliciousBar} from './search-bar'; diff --git a/frontend/src/search-bar.ts b/frontend/src/search-bar.ts new file mode 100644 index 00000000..6ba031d3 --- /dev/null +++ b/frontend/src/search-bar.ts @@ -0,0 +1,60 @@ +import {LitElement, html, css} from 'lit'; +import {customElement, property} from 'lit/decorators.js'; +import {SearchaliciousSearchMixin} from './search-ctl'; + +/** + * The search bar element. + * + */ +@customElement('searchalicious-bar') +export class SearchaliciousBar extends SearchaliciousSearchMixin(LitElement) { + static override styles = css` + :host { + display: block; + border: solid 1px gray; + padding: 16px; + } + `; + + /** + * Place holder in search bar + */ + @property() + placeholder = 'Search...'; + + override render() { + return html` + + + `; + } + + private _onClick() { + // launch search + this.search(); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'searchalicious-bar': SearchaliciousBar; + } +} diff --git a/frontend/src/search-ctl.ts b/frontend/src/search-ctl.ts new file mode 100644 index 00000000..0c7aac4f --- /dev/null +++ b/frontend/src/search-ctl.ts @@ -0,0 +1,103 @@ +import {LitElement} from 'lit'; +import {property, state} from 'lit/decorators.js'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Constructor = new (...args: any[]) => T; + +export declare class SearchaliciousSearchInterface { + query: string; + name: string; + baseUrl: string; + langs: string; + pageSize: Number; + + search(): Promise; +} + +export const SearchaliciousSearchMixin = >( + superClass: T +) => { + /** + * The search mixin, encapsulate the logic of dialog with server + */ + class SearchaliciousSearchMixinClass extends superClass { + /** + * Query that will be sent to searchalicious + */ + @state() + query = ''; + + /** + * The name of this search + */ + @property() + name = 'searchalicious'; + + /** + * The base api url + */ + @property() + baseUrl = '/'; + + /** + * Separated list of languages + */ + @property() + langs = 'en'; + + // TODO: should be on results element instead + @property({type: Number}) + pageSize: Number = 10; + + @state() + pageCount?: Number; + + @state() + results?: {}[]; + + @state() + count?: Number; + + // TODO: this should be on results element instead + _searchUrl() { + // remove trailing slash + const baseUrl = this.baseUrl.replace(/\/+$/, ''); + // build parameters + const params = { + q: this.query, + langs: this.langs, + page_size: this.pageSize.toString(), + }; + const queryStr = Object.entries(params) + .map( + ([key, value]) => + `${encodeURIComponent(key)}=${encodeURIComponent(value)}` + ) + .join('&'); + // FIXME: handle page + return `${baseUrl}/search?${queryStr}`; + } + + /** + * Launching search + */ + async search() { + const response = await fetch(this._searchUrl()); + const data = await response.json(); + this.results = data.hits; + this.count = data.count; + this.pageCount = data.page_count; + // dispatch an event with the results + this.dispatchEvent(new CustomEvent(`searchalicious-result`, {})); + } + } + + return SearchaliciousSearchMixinClass as Constructor & + T; +}; + +declare global { + interface GlobalEventHandlersEventMap { + 'searchalicious-result': CustomEvent<{}>; + } +} diff --git a/frontend/src/test/search-a-licious_test.ts b/frontend/src/test/search-a-licious_test.ts deleted file mode 100644 index aaf9268a..00000000 --- a/frontend/src/test/search-a-licious_test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -import {SearchALicious} from '../search-a-licious.js'; - -import {fixture, assert} from '@open-wc/testing'; -import {html} from 'lit/static-html.js'; - -suite('search-a-licious', () => { - test('is defined', () => { - const el = document.createElement('search-a-licious'); - assert.instanceOf(el, SearchALicious); - }); - - test('renders with default values', async () => { - const el = await fixture(html``); - assert.shadowDom.equal( - el, - ` -

Hello, World!

- - - ` - ); - }); - - test('renders with a set name', async () => { - const el = await fixture( - html`` - ); - assert.shadowDom.equal( - el, - ` -

Hello, Test!

- - - ` - ); - }); - - test('handles a click', async () => { - const el = (await fixture( - html`` - )) as SearchALicious; - const button = el.shadowRoot!.querySelector('button')!; - button.click(); - await el.updateComplete; - assert.shadowDom.equal( - el, - ` -

Hello, World!

- - - ` - ); - }); - - test('styling applied', async () => { - const el = (await fixture( - html`` - )) as SearchALicious; - await el.updateComplete; - assert.equal(getComputedStyle(el).paddingTop, '16px'); - }); -}); diff --git a/frontend/src/test/search-bar_test.ts b/frontend/src/test/search-bar_test.ts new file mode 100644 index 00000000..04c3e8c0 --- /dev/null +++ b/frontend/src/test/search-bar_test.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ + +import {SearchaliciousBar} from '../search-bar'; + +import {fixture, assert} from '@open-wc/testing'; +import {html} from 'lit/static-html.js'; + +suite('searchalicious-bar', () => { + test('is defined', () => { + const el = document.createElement('searchalicious-bar'); + assert.instanceOf(el, SearchaliciousBar); + }); + + test('renders with default values', async () => { + const el = await fixture(html``); + assert.shadowDom.equal( + el, + ` + + + ` + ); + }); +}); diff --git a/frontend/web-dev-server.config.js b/frontend/web-dev-server.config.js index 0c0fd21a..b703793f 100644 --- a/frontend/web-dev-server.config.js +++ b/frontend/web-dev-server.config.js @@ -1,10 +1,5 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - import {legacyPlugin} from '@web/dev-server-legacy'; +import { rollupPluginHTML as html } from '@web/rollup-plugin-html'; const mode = process.env.MODE || 'dev'; if (!['dev', 'prod'].includes(mode)) { @@ -14,6 +9,7 @@ if (!['dev', 'prod'].includes(mode)) { export default { nodeResolve: {exportConditions: mode === 'dev' ? ['development'] : []}, preserveSymlinks: true, + output: {dir: 'public'}, plugins: [ legacyPlugin({ polyfills: { From ab99da173ab24381ac68e2877cd5fa4cda7078c8 Mon Sep 17 00:00:00 2001 From: Alex Garel Date: Wed, 10 Apr 2024 16:13:54 +0200 Subject: [PATCH 2/6] feat: skip updates option to load data --- Makefile | 2 +- README.md | 4 ++++ app/_import.py | 4 +++- app/cli/main.py | 5 +++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 76efea58..42603ec5 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,7 @@ guard-%: # guard clause for targets that require an environment variable (usuall import-dataset: guard-filepath @echo "🔎 Importing data …" - ${DOCKER_COMPOSE} run --rm api python3 -m app import /opt/search/data/${filepath} --num-processes=2 + ${DOCKER_COMPOSE} run --rm api python3 -m app import /opt/search/data/${filepath} ${args} --num-processes=2 #-------# diff --git a/README.md b/README.md index f0a6feb1..7512a0d3 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ If you get errors, try adding more RAM (12GB works well if you have that spare), Typical import time is 45-60 minutes. +If you want to skip updates (eg. because you don't have a Redis installed), +use `make import-dataset filepath='products.jsonl.gz' args="--skip-updates"` + + ## Fundings This project has received financial support from the NGI Search (New Generation Internet) program, funded by the European Commission. diff --git a/app/_import.py b/app/_import.py index 469a8d8d..3576045c 100644 --- a/app/_import.py +++ b/app/_import.py @@ -405,6 +405,7 @@ def run_full_import( num_processes: int, config: IndexConfig, num_items: int | None = None, + skip_updates: bool = False, ): """Run a full data import from a JSONL. @@ -447,7 +448,8 @@ def run_full_import( with Pool(num_processes) as pool: pool.starmap(import_parallel, args) # update with last index updates (hopefully since the jsonl) - get_redis_updates(es_client, next_index, config) + if not skip_updates: + get_redis_updates(es_client, next_index, config) # make alias point to new index update_alias(es_client, next_index, config.index.name) diff --git a/app/cli/main.py b/app/cli/main.py index 7d1c8bd3..17c5fc1e 100644 --- a/app/cli/main.py +++ b/app/cli/main.py @@ -15,6 +15,10 @@ def import_data( dir_okay=False, help="Path of the JSONL data file", ), + skip_updates: bool = typer.Option( + default=False, + help="Skip fetching fresh records from redis", + ), num_processes: int = typer.Option( default=2, help="How many import processes to run in parallel" ), @@ -63,6 +67,7 @@ def import_data( num_processes, index_config, num_items=num_items, + skip_updates=skip_updates, ) end_time = time.perf_counter() logger.info("Import time: %s seconds", end_time - start_time) From 92eb8312592a7630478186d805247f9714ab70a2 Mon Sep 17 00:00:00 2001 From: Alex Garel Date: Wed, 10 Apr 2024 16:16:17 +0200 Subject: [PATCH 3/6] build: serving html files and distinguish build and dist --- .gitignore | 2 +- confs/nginx.conf | 6 +++++- frontend/Dockerfile | 2 +- frontend/public/{ => dist}/.empty | 0 frontend/{src => public}/off.html | 2 +- frontend/rollup.config.js | 2 +- frontend/tsconfig.json | 2 +- frontend/web-dev-server.config.js | 5 +++++ 8 files changed, 15 insertions(+), 6 deletions(-) rename frontend/public/{ => dist}/.empty (100%) rename frontend/{src => public}/off.html (99%) diff --git a/.gitignore b/.gitignore index 99667497..5c02e290 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,4 @@ dmypy.json /frontend/node_modules/ /frontend/lib/ /frontend/test/ -/frontend/public/ +/frontend/public/dist/ diff --git a/confs/nginx.conf b/confs/nginx.conf index 6ecb67c8..c7b95295 100644 --- a/confs/nginx.conf +++ b/confs/nginx.conf @@ -27,6 +27,10 @@ server { # this is the internal Docker DNS, cache only for 30s resolver 127.0.0.11 valid=30s; + # web server dev has specific urls, we want to redirect them to static + rewrite ^/(__wds-|__web-dev-server)(.*)$ /static/$1$2 last; + + # Static files - in DEV = node server location /static${DEV_UI_SUFFIX} { proxy_set_header Host $host; @@ -35,7 +39,7 @@ server { set $search_nodejs search_nodejs; # rewrite to get rid of /static # double $ to avoid being interpreted by interpolation - rewrite ^/static${DEV_UI_SUFFIX}/(.*)$ /public/$1 break; + # rewrite ^/static${DEV_UI_SUFFIX}/(.*)$ /$1 break; # important do not add a / at the end for node is picky on not having '//' in url proxy_pass http://$search_nodejs:8000; } diff --git a/frontend/Dockerfile b/frontend/Dockerfile index c798d878..c784d6a1 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -46,7 +46,7 @@ COPY --chown=node:node tsconfig.json /opt/search-a-licious/tsconfig.json COPY --chown=node:node rollup.config.js /opt/search-a-licious/rollup.config.js # build for production # no need of a public url, we are at the root -RUN npm run build && npm run bundle +RUN rm -rf public/dist/* && npm run build && npm run bundle CMD ["npm", "run", "serve"] # nginx diff --git a/frontend/public/.empty b/frontend/public/dist/.empty similarity index 100% rename from frontend/public/.empty rename to frontend/public/dist/.empty diff --git a/frontend/src/off.html b/frontend/public/off.html similarity index 99% rename from frontend/src/off.html rename to frontend/public/off.html index e0da1fc1..6cd20e41 100644 --- a/frontend/src/off.html +++ b/frontend/public/off.html @@ -9,7 +9,7 @@ href="https://static.openfoodfacts.org/css/dist/app-ltr.css?v=1695281325" data-base-layout="true" /> - +
diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js index d80411a5..c3db3422 100644 --- a/frontend/rollup.config.js +++ b/frontend/rollup.config.js @@ -10,7 +10,7 @@ import resolve from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; export default { - input: 'public/search-a-licious.js', + input: 'public/dist/search-a-licious.js', output: { file: 'search-a-licious.bundled.js', format: 'esm', diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 359c777b..c282455d 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -8,7 +8,7 @@ "declarationMap": true, "sourceMap": true, "inlineSources": true, - "outDir": "./public", + "outDir": "./public/dist", "rootDir": "./src", "strict": true, "noUnusedLocals": true, diff --git a/frontend/web-dev-server.config.js b/frontend/web-dev-server.config.js index b703793f..1869829e 100644 --- a/frontend/web-dev-server.config.js +++ b/frontend/web-dev-server.config.js @@ -8,8 +8,13 @@ if (!['dev', 'prod'].includes(mode)) { export default { nodeResolve: {exportConditions: mode === 'dev' ? ['development'] : []}, + //webSocketServer: {options: { path: sockPath }}, + open: false, preserveSymlinks: true, + watch: true, output: {dir: 'public'}, + rootDir: 'public/', + basePath: '/static', plugins: [ legacyPlugin({ polyfills: { From 9139d2d48f07cfd9af14eb59eeaf5d399e21b330 Mon Sep 17 00:00:00 2001 From: Alex Garel Date: Wed, 10 Apr 2024 17:24:55 +0200 Subject: [PATCH 4/6] feat: wip on search bar feature --- frontend/src/search-bar.ts | 11 ++++++++--- frontend/src/search-ctl.ts | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/search-bar.ts b/frontend/src/search-bar.ts index 6ba031d3..5adf463f 100644 --- a/frontend/src/search-bar.ts +++ b/frontend/src/search-bar.ts @@ -27,10 +27,11 @@ export class SearchaliciousBar extends SearchaliciousSearchMixin(LitElement) { - `; } private _onQueryChange(event: Event) { this.query = (event.target as HTMLInputElement).value; } - - private _onSearchClick() { - // launch search - this.search(); + private _onKeyUp(event: Event) { + const kbd_event = event as KeyboardEvent; + if (kbd_event.key === 'Enter') { + // launch search + this.search(); + } } } diff --git a/frontend/src/search-button.ts b/frontend/src/search-button.ts new file mode 100644 index 00000000..8ab7ac90 --- /dev/null +++ b/frontend/src/search-button.ts @@ -0,0 +1,44 @@ +import {LitElement, html} from 'lit'; +import {customElement, property} from 'lit/decorators.js'; +import {SearchaliciousEvents} from './enums'; + +/** + * An optional search button element. + * + * @slot - goes in button contents, default to "Search" string + */ +@customElement('searchalicious-button') +export class SearchaliciousButton extends LitElement { + /** + * the search we should trigger, + * this corresponds to `name` attribute of corresponding search-bar + */ + @property() + search_name = 'searchalicious'; + + override render() { + return html` + + `; + } + + private _onClick() { + const detail = {search_name: this.search_name}; + // fire the search event + const event = new CustomEvent(SearchaliciousEvents.LAUNCH_SEARCH, { + bubbles: true, + composed: true, + detail: detail, + }); + const success = this.dispatchEvent(event); + console.log(success); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'searchalicious-button': SearchaliciousButton; + } +} diff --git a/frontend/src/search-ctl.ts b/frontend/src/search-ctl.ts index 13dad9a8..ee36b06b 100644 --- a/frontend/src/search-ctl.ts +++ b/frontend/src/search-ctl.ts @@ -1,5 +1,6 @@ import {LitElement} from 'lit'; import {property, state} from 'lit/decorators.js'; +import {SearchaliciousEvents} from './enums'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type Constructor = new (...args: any[]) => T; @@ -9,6 +10,7 @@ export declare class SearchaliciousSearchInterface { name: string; baseUrl: string; langs: string; + index: string; pageSize: Number; search(): Promise; @@ -45,18 +47,26 @@ export const SearchaliciousSearchMixin = >( @property() langs = 'en'; + /** + * index to query + */ + @property() + index?: string; + // TODO: should be on results element instead @property({type: Number, attribute: 'page-size'}) pageSize: Number = 10; @state() - pageCount?: Number; + _pageCount?: Number; @state() - results?: {}[]; + _results?: {}[]; @state() - count?: Number; + _count?: Number; + + _event_setups: number[] = []; // TODO: this should be on results element instead _searchUrl() { @@ -67,28 +77,84 @@ export const SearchaliciousSearchMixin = >( q: this.query, langs: this.langs, page_size: this.pageSize.toString(), + index: this.index, }; const queryStr = Object.entries(params) + .filter( + ([_, value]) => value != null // null or undefined + ) .map( ([key, value]) => - `${encodeURIComponent(key)}=${encodeURIComponent(value)}` + `${encodeURIComponent(key)}=${encodeURIComponent(value!)}` ) + .sort() // for perdictability in tests ! .join('&'); - // FIXME: handle page + // FIXME: handle pagination return `${baseUrl}/search?${queryStr}`; } + _registerEventHandlers() { + window.addEventListener(SearchaliciousEvents.LAUNCH_SEARCH, (event) => + this._handleSearch(event) + ); + this._event_setups.pop(); + } + + // connect to our specific events + override connectedCallback() { + super.connectedCallback(); + this._event_setups.push( + window.requestAnimationFrame(() => this._registerEventHandlers()) + ); + } + // connect to our specific events + override disconnectedCallback() { + super.disconnectedCallback(); + if (this._event_setups) { + window.cancelAnimationFrame(this._event_setups.pop()!); // cancel one registration + } else { + window.removeEventListener( + SearchaliciousEvents.LAUNCH_SEARCH, + (event) => this._handleSearch(event) + ); + } + } + + /** + * External component (like the search button) + * can use the `searchalicious-search` event + * to trigger a search. + * It must have the search name in it's data. + */ + _handleSearch(event: Event) { + const detail = (event as CustomEvent).detail; + if (detail.search_name === this.name) { + this.search(); + } + } + /** * Launching search */ async search() { const response = await fetch(this._searchUrl()); const data = await response.json(); - this.results = data.hits; - this.count = data.count; - this.pageCount = data.page_count; + this._results = data.hits; + this._count = data.count; + this._pageCount = data.page_count; + const detail = { + results: this._results, + count: this._count, + pageCount: this._pageCount, + }; // dispatch an event with the results - this.dispatchEvent(new CustomEvent(`searchalicious-result`, {})); + this.dispatchEvent( + new CustomEvent(SearchaliciousEvents.NEW_RESULT, { + bubbles: true, + composed: true, + detail: detail, + }) + ); } } diff --git a/frontend/src/test/search-bar_test.ts b/frontend/src/test/search-bar_test.ts index 7507dbee..e4b60d0c 100644 --- a/frontend/src/test/search-bar_test.ts +++ b/frontend/src/test/search-bar_test.ts @@ -1,12 +1,6 @@ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - import {SearchaliciousBar} from '../search-bar'; -import {fixture, assert} from '@open-wc/testing'; +import {fixture, assert, expect} from '@open-wc/testing'; import {html} from 'lit/static-html.js'; suite('searchalicious-bar', () => { @@ -17,6 +11,30 @@ suite('searchalicious-bar', () => { test('renders with default values', async () => { const el = await fixture(html``); + assert.shadowDom.equal( + el, + ` + + ` + ); + const input = el.shadowRoot!.querySelector('input'); + expect(input).to.have.value(''); + const bar = el as SearchaliciousBar; + assert.equal(bar.query, ''); + assert.equal(bar.index, undefined); + }); + + test('renders with custom attributes', async () => { + const el = await fixture( + html`` + ); //const input = (el.getElementsByTagName('input')[0] as HTMLInputElement); //assert.equal(input.value, 'fixme'); assert.shadowDom.equal( @@ -24,10 +42,36 @@ suite('searchalicious-bar', () => { ` ` ); + const bar = el as SearchaliciousBar; + assert.equal(bar.query, ''); + assert.equal(bar.index, 'foo'); + }); + + test('text input in query', async () => { + const el = await fixture(html``); + const input = el.shadowRoot!.querySelector('input'); + input!.value = 'test'; + input!.dispatchEvent(new Event('input')); + const bar = el as SearchaliciousBar; + assert.equal(bar.query, 'test'); + }); + + test('_searchUrl computation', async () => { + const el = await fixture( + html`` + ); + const input = el.shadowRoot!.querySelector('input'); + input!.value = 'test'; + input!.dispatchEvent(new Event('input')); + const bar = el as SearchaliciousBar; + assert.equal( + (bar as any)['_searchUrl'](), + '/search?index=foo&langs=en&page_size=10&q=test' + ); }); }); diff --git a/frontend/src/test/search-button_test.ts b/frontend/src/test/search-button_test.ts new file mode 100644 index 00000000..8af7c4c2 --- /dev/null +++ b/frontend/src/test/search-button_test.ts @@ -0,0 +1,27 @@ +import {SearchaliciousButton} from '../search-button'; + +import {fixture, assert} from '@open-wc/testing'; +import {html} from 'lit/static-html.js'; + +suite('searchalicious-button', () => { + test('is defined', () => { + const el = document.createElement('searchalicious-button'); + assert.instanceOf(el, SearchaliciousButton); + }); + + test('renders with default values', async () => { + const el = await fixture( + html`` + ); + assert.shadowDom.equal( + el, + ` + + ` + ); + }); +});