From ee60b9f01d22f53c34f9d285c4433590e980dcc8 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Thu, 17 Nov 2022 09:38:52 -0500 Subject: [PATCH 01/23] Fix wcag github action mapbox issues (#337) paired with the changes in this [PR](https://github.com/yext/slapshot-reusable-workflows/pull/22) in WCAG workflow, this PR updates the WCAG github action in the repo to pass in the mapbox key. Also updated the wcag test to exclude checking elements coming from mapbox canvas container as any potential violations coming from there is outside of our repo's control. WCAG github action also run on pull request to feature branch now. J=SLAP-2458 TEST=auto see that WCAG github action now passes --- .github/workflows/wcag_test.yml | 3 ++- .storybook/test-runner.ts | 34 ++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.github/workflows/wcag_test.yml b/.github/workflows/wcag_test.yml index 90d26ade8..afc25481c 100644 --- a/.github/workflows/wcag_test.yml +++ b/.github/workflows/wcag_test.yml @@ -2,12 +2,13 @@ name: WCAG tests on: pull_request: - branches: [main, develop] + branches: [main, develop, feature/*] jobs: call_wcag_test: uses: yext/slapshot-reusable-workflows/.github/workflows/wcag_test.yml@v1 secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} with: build_script: ./tests/scripts/start-storybook.sh diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index 6aa34baef..ac1728692 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -1,28 +1,36 @@ import { injectAxe, checkA11y } from 'axe-playwright'; import { Page } from 'playwright-core'; import { runOnly } from './wcagConfig'; +import { TestContext, TestRunnerConfig } from '@storybook/test-runner'; /** * See https://storybook.js.org/docs/react/writing-tests/test-runner#test-hook-api-experimental * to learn more about the test-runner hooks API. */ -const renderFunctions = { +const renderFunctions: TestRunnerConfig = { async preRender(page: Page) { await injectAxe(page); }, - async postRender(page: Page, context) { - await checkA11y(page, '#root', { - axeOptions: { - runOnly, - rules: { - 'color-contrast': { enabled: context.name !== 'Loading' } - }, - }, - detailedReport: true, - detailedReportOptions: { - html: true, + async postRender(page: Page, context: TestContext) { + await checkA11y( + page, + { + include: ['#root'], + exclude: ['#root .mapboxgl-canvas-container'], }, - }); + { + axeOptions: { + runOnly, + rules: { + 'color-contrast': { enabled: context.name !== 'Loading' }, + }, + }, + detailedReport: true, + detailedReportOptions: { + html: true, + }, + } + ); }, }; From 3748b153cfbb1ec3dd2c398ab9b971b6a81158e7 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Thu, 17 Nov 2022 09:44:51 -0500 Subject: [PATCH 02/23] Deprecate LocationBias component (#338) this pr mark LocationBias as deprecated in favor of the new Geolocation component. I checked if storybook have some feature to inform user that a component is deprecated, there's an open feature request: https://github.com/storybookjs/storybook/issues/9721 for displaying @deprecated props but nothing official yet. I did come across a custom addon: https://storybook.js.org/addons/@etchteam/storybook-addon-status which seems to work quite nicely. Show "Deprecated" status for location bias stories: Screen Shot 2022-11-16 at 4 31 20 PM J=SLAP-2448 TEST=none --- .storybook/main.js | 1 + docs/search-ui-react.locationbias.md | 5 +++ .../search-ui-react.locationbiascssclasses.md | 5 +++ docs/search-ui-react.locationbiasprops.md | 5 +++ etc/search-ui-react.api.md | 6 +-- package-lock.json | 40 +++++++++++++++++++ package.json | 3 +- src/components/LocationBias.tsx | 6 +++ tests/components/LocationBias.stories.tsx | 7 +++- 9 files changed, 73 insertions(+), 5 deletions(-) diff --git a/.storybook/main.js b/.storybook/main.js index 545d0e4d2..0b27d3606 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -3,6 +3,7 @@ module.exports = { '../tests/**/*.stories.tsx' ], addons: [ + '@etchteam/storybook-addon-status', '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', diff --git a/docs/search-ui-react.locationbias.md b/docs/search-ui-react.locationbias.md index 7b7064a82..70cde1e2f 100644 --- a/docs/search-ui-react.locationbias.md +++ b/docs/search-ui-react.locationbias.md @@ -4,6 +4,11 @@ ## LocationBias() function +> Warning: This API is now obsolete. +> +> LocationBias component has been superseded by Geolocation component. +> + A React Component which displays and collects location information in order to bias searches. Signature: diff --git a/docs/search-ui-react.locationbiascssclasses.md b/docs/search-ui-react.locationbiascssclasses.md index 691f9b288..91a36b6fa 100644 --- a/docs/search-ui-react.locationbiascssclasses.md +++ b/docs/search-ui-react.locationbiascssclasses.md @@ -4,6 +4,11 @@ ## LocationBiasCssClasses interface +> Warning: This API is now obsolete. +> +> LocationBias component has been superseded by Geolocation component. +> + The CSS class interface for the [LocationBias()](./search-ui-react.locationbias.md) component. Signature: diff --git a/docs/search-ui-react.locationbiasprops.md b/docs/search-ui-react.locationbiasprops.md index 98ecc0b9f..fcc56ec34 100644 --- a/docs/search-ui-react.locationbiasprops.md +++ b/docs/search-ui-react.locationbiasprops.md @@ -4,6 +4,11 @@ ## LocationBiasProps interface +> Warning: This API is now obsolete. +> +> LocationBias component has been superseded by Geolocation component. +> + The props for the [LocationBias()](./search-ui-react.locationbias.md) component. Signature: diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index a05aa14d7..766c23385 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -335,10 +335,10 @@ export interface HighlightedValueCssClasses { // @public export function isCtaData(data: unknown): data is CtaData; -// @public +// @public @deprecated export function LocationBias({ geolocationOptions, customCssClasses }: LocationBiasProps): JSX.Element | null; -// @public +// @public @deprecated export interface LocationBiasCssClasses { // (undocumented) button?: string; @@ -352,7 +352,7 @@ export interface LocationBiasCssClasses { source?: string; } -// @public +// @public @deprecated export interface LocationBiasProps { customCssClasses?: LocationBiasCssClasses; geolocationOptions?: PositionOptions; diff --git a/package-lock.json b/package-lock.json index f45415d02..597379969 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@babel/preset-env": "^7.14.7", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.14.5", + "@etchteam/storybook-addon-status": "^4.2.2", "@percy/cli": "^1.8.0", "@percy/storybook": "^4.3.3", "@reduxjs/toolkit": "^1.8.6", @@ -2282,6 +2283,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@etchteam/storybook-addon-status": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@etchteam/storybook-addon-status/-/storybook-addon-status-4.2.2.tgz", + "integrity": "sha512-iCOoA0+Izu/SixxjjJ9BB4YBVT2reCJ/80dXHBiCqoGSPTAvYGeeoQKexC913eSFv/0u3u4lv5fRZN/KMPAurw==", + "dev": true, + "dependencies": { + "@storybook/addons": "^6.2.9", + "@storybook/api": "^6.2.9", + "@storybook/client-logger": "^6.2.9", + "@storybook/components": "^6.2.9", + "@storybook/core-events": "^6.2.9", + "@storybook/theming": "^6.2.9", + "core-js": "^3.0.1", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "util-deprecate": "^1.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17.0.2 || ^18.0.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -34700,6 +34722,24 @@ } } }, + "@etchteam/storybook-addon-status": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@etchteam/storybook-addon-status/-/storybook-addon-status-4.2.2.tgz", + "integrity": "sha512-iCOoA0+Izu/SixxjjJ9BB4YBVT2reCJ/80dXHBiCqoGSPTAvYGeeoQKexC913eSFv/0u3u4lv5fRZN/KMPAurw==", + "dev": true, + "requires": { + "@storybook/addons": "^6.2.9", + "@storybook/api": "^6.2.9", + "@storybook/client-logger": "^6.2.9", + "@storybook/components": "^6.2.9", + "@storybook/core-events": "^6.2.9", + "@storybook/theming": "^6.2.9", + "core-js": "^3.0.1", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "util-deprecate": "^1.0.2" + } + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", diff --git a/package.json b/package.json index 95049f18b..0401a0c25 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@babel/preset-env": "^7.14.7", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.14.5", + "@etchteam/storybook-addon-status": "^4.2.2", "@percy/cli": "^1.8.0", "@percy/storybook": "^4.3.3", "@reduxjs/toolkit": "^1.8.6", @@ -134,7 +135,6 @@ "restoreMocks": true }, "dependencies": { - "lodash": "^4.17.21", "@microsoft/api-documenter": "^7.15.3", "@microsoft/api-extractor": "^7.19.4", "@reach/auto-id": "^0.18.0", @@ -142,6 +142,7 @@ "@tailwindcss/forms": "^0.5.0", "@yext/analytics": "^0.2.0-beta.3", "classnames": "^2.3.1", + "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", "prop-types": "^15.8.1", "react-collapsed": "^3.3.0", diff --git a/src/components/LocationBias.tsx b/src/components/LocationBias.tsx index 577feb3e4..81992aa7d 100644 --- a/src/components/LocationBias.tsx +++ b/src/components/LocationBias.tsx @@ -9,6 +9,8 @@ import LoadingIndicator from '../icons/LoadingIndicator'; * The CSS class interface for the {@link LocationBias} component. * * @public + * + * @deprecated LocationBias component has been superseded by Geolocation component. */ export interface LocationBiasCssClasses { locationBiasContainer?: string, @@ -30,6 +32,8 @@ const builtInCssClasses: Readonly = { * The props for the {@link LocationBias} component. * * @public + * + * @deprecated LocationBias component has been superseded by Geolocation component. */ export interface LocationBiasProps { /** Configuration used when collecting the user's location. @@ -45,6 +49,8 @@ export interface LocationBiasProps { * * @public * + * @deprecated LocationBias component has been superseded by Geolocation component. + * * @param props - {@link LocationBiasProps} * @returns A react component for Location Bias */ diff --git a/tests/components/LocationBias.stories.tsx b/tests/components/LocationBias.stories.tsx index ff5ce6c8b..ef61c2be3 100644 --- a/tests/components/LocationBias.stories.tsx +++ b/tests/components/LocationBias.stories.tsx @@ -15,7 +15,12 @@ const meta: ComponentMeta = { geolocationOptions: { control: false } - } + }, + parameters: { + status: { + type: 'deprecated', + } + }, }; export default meta; From c7e2d78b26f33bba9478eca87c39008d177b3e98 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Fri, 18 Nov 2022 10:30:04 -0500 Subject: [PATCH 03/23] Fix visual coverage test in Github Actions (#339) In addition to the changes in the [workflow PR](https://github.com/yext/slapshot-reusable-workflows/pull/24), this up updates run-tests github action and coverage github action to pass mapbox api key so visual coverage test works as expected. J=SLAP-2467 TEST=auto see that run-tests github action and coverage github action passed -- looked into the logs, no false positive / errors related to visual coverage. --- .github/workflows/coverage.yml | 1 + .github/workflows/run-tests.yml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f5898e149..4ead6d09d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -15,6 +15,7 @@ jobs: test_script: npm run test secrets: caller_github_token: ${{ secrets.GITHUB_TOKEN }} + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} individual_coverage_percentages: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1e1b8207e..d958880f4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,11 +13,15 @@ jobs: npm i -D react@18 react-dom@18 npm run build node_matrix: '["14.x", "16.x", "18.x"]' + secrets: + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} call_run_tests-react-17: uses: yext/slapshot-reusable-workflows/.github/workflows/run_tests.yml@v1 with: node_matrix: '["14.x", "16.x", "18.x"]' + secrets: + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} call_run_tests-react-16: uses: yext/slapshot-reusable-workflows/.github/workflows/run_tests.yml@v1 @@ -26,3 +30,5 @@ jobs: npm i -D react@16.14 react-dom@16.14 npm run build node_matrix: '["14.x", "16.x", "18.x"]' + secrets: + MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} From ff5293e81abbb02de1299d733e730089e2e2d7d7 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:00:02 -0500 Subject: [PATCH 04/23] Add Geolocation component (#336) This PR adds Geolocation component to the repo, with jest tests and storybook stories. Note: waiting on product to get SVG design for the default geolocation icon. Edit: Will update in another PR J=SLAP-2396,SLAP-2397,SLAP-2398 TEST=manual&auto - tested manually through test site in Products page - jest tests passed and storybook stories display component as expected --- .storybook/preview.js | 1 + docs/search-ui-react.geolocation_2.md | 26 ++ ...h-ui-react.geolocationcssclasses.button.md | 11 + ...locationcssclasses.geolocationcontainer.md | 11 + ...act.geolocationcssclasses.iconcontainer.md | 11 + docs/search-ui-react.geolocationcssclasses.md | 22 ++ ...react.geolocationprops.customcssclasses.md | 13 + ...-react.geolocationprops.geolocationicon.md | 13 + ...act.geolocationprops.geolocationoptions.md | 13 + ...h-ui-react.geolocationprops.handleclick.md | 13 + .../search-ui-react.geolocationprops.label.md | 13 + docs/search-ui-react.geolocationprops.md | 25 ++ ...search-ui-react.geolocationprops.radius.md | 13 + docs/search-ui-react.md | 3 + etc/search-ui-react.api.md | 24 ++ src/components/AppliedFilters.tsx | 4 +- src/components/Geolocation.tsx | 87 +++++++ src/components/index.ts | 6 + src/hooks/useGeolocationHandler.ts | 81 ++++++ test-site/src/pages/ProductsPage.tsx | 4 +- tests/components/Geolocation.stories.tsx | 45 ++++ tests/components/Geolocation.test.tsx | 246 ++++++++++++++++++ 22 files changed, 681 insertions(+), 4 deletions(-) create mode 100644 docs/search-ui-react.geolocation_2.md create mode 100644 docs/search-ui-react.geolocationcssclasses.button.md create mode 100644 docs/search-ui-react.geolocationcssclasses.geolocationcontainer.md create mode 100644 docs/search-ui-react.geolocationcssclasses.iconcontainer.md create mode 100644 docs/search-ui-react.geolocationcssclasses.md create mode 100644 docs/search-ui-react.geolocationprops.customcssclasses.md create mode 100644 docs/search-ui-react.geolocationprops.geolocationicon.md create mode 100644 docs/search-ui-react.geolocationprops.geolocationoptions.md create mode 100644 docs/search-ui-react.geolocationprops.handleclick.md create mode 100644 docs/search-ui-react.geolocationprops.label.md create mode 100644 docs/search-ui-react.geolocationprops.md create mode 100644 docs/search-ui-react.geolocationprops.radius.md create mode 100644 src/components/Geolocation.tsx create mode 100644 src/hooks/useGeolocationHandler.ts create mode 100644 tests/components/Geolocation.stories.tsx create mode 100644 tests/components/Geolocation.test.tsx diff --git a/.storybook/preview.js b/.storybook/preview.js index 9bf99b604..f0e0b4f44 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -36,6 +36,7 @@ export const parameters = { 'AlternativeVerticals', 'SpellCheck', 'ResultsCount', + 'Geolocation', 'LocationBias', 'Dropdown' ] diff --git a/docs/search-ui-react.geolocation_2.md b/docs/search-ui-react.geolocation_2.md new file mode 100644 index 000000000..5143a96a4 --- /dev/null +++ b/docs/search-ui-react.geolocation_2.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [Geolocation\_2](./search-ui-react.geolocation_2.md) + +## Geolocation\_2() function + +A React Component which collects location information to create a location filter and perform a new search. + +Signature: + +```typescript +export declare function Geolocation({ geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, }: GeolocationProps): JSX.Element | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, } | [GeolocationProps](./search-ui-react.geolocationprops.md) | | + +Returns: + +JSX.Element \| null + +A react component for geolocation + diff --git a/docs/search-ui-react.geolocationcssclasses.button.md b/docs/search-ui-react.geolocationcssclasses.button.md new file mode 100644 index 000000000..a140dab75 --- /dev/null +++ b/docs/search-ui-react.geolocationcssclasses.button.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) > [button](./search-ui-react.geolocationcssclasses.button.md) + +## GeolocationCssClasses.button property + +Signature: + +```typescript +button?: string; +``` diff --git a/docs/search-ui-react.geolocationcssclasses.geolocationcontainer.md b/docs/search-ui-react.geolocationcssclasses.geolocationcontainer.md new file mode 100644 index 000000000..377c506d5 --- /dev/null +++ b/docs/search-ui-react.geolocationcssclasses.geolocationcontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) > [geolocationContainer](./search-ui-react.geolocationcssclasses.geolocationcontainer.md) + +## GeolocationCssClasses.geolocationContainer property + +Signature: + +```typescript +geolocationContainer?: string; +``` diff --git a/docs/search-ui-react.geolocationcssclasses.iconcontainer.md b/docs/search-ui-react.geolocationcssclasses.iconcontainer.md new file mode 100644 index 000000000..467401468 --- /dev/null +++ b/docs/search-ui-react.geolocationcssclasses.iconcontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) > [iconContainer](./search-ui-react.geolocationcssclasses.iconcontainer.md) + +## GeolocationCssClasses.iconContainer property + +Signature: + +```typescript +iconContainer?: string; +``` diff --git a/docs/search-ui-react.geolocationcssclasses.md b/docs/search-ui-react.geolocationcssclasses.md new file mode 100644 index 000000000..d56002fda --- /dev/null +++ b/docs/search-ui-react.geolocationcssclasses.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) + +## GeolocationCssClasses interface + +The CSS class interface for the Geolocation component. + +Signature: + +```typescript +export interface GeolocationCssClasses +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [button?](./search-ui-react.geolocationcssclasses.button.md) | string | (Optional) | +| [geolocationContainer?](./search-ui-react.geolocationcssclasses.geolocationcontainer.md) | string | (Optional) | +| [iconContainer?](./search-ui-react.geolocationcssclasses.iconcontainer.md) | string | (Optional) | + diff --git a/docs/search-ui-react.geolocationprops.customcssclasses.md b/docs/search-ui-react.geolocationprops.customcssclasses.md new file mode 100644 index 000000000..b4b7a71f1 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.customcssclasses.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [customCssClasses](./search-ui-react.geolocationprops.customcssclasses.md) + +## GeolocationProps.customCssClasses property + +CSS classes for customizing the component styling. + +Signature: + +```typescript +customCssClasses?: GeolocationCssClasses; +``` diff --git a/docs/search-ui-react.geolocationprops.geolocationicon.md b/docs/search-ui-react.geolocationprops.geolocationicon.md new file mode 100644 index 000000000..afcf079c9 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.geolocationicon.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [GeolocationIcon](./search-ui-react.geolocationprops.geolocationicon.md) + +## GeolocationProps.GeolocationIcon property + +Custom icon component to display along with the button. + +Signature: + +```typescript +GeolocationIcon?: React.FunctionComponent; +``` diff --git a/docs/search-ui-react.geolocationprops.geolocationoptions.md b/docs/search-ui-react.geolocationprops.geolocationoptions.md new file mode 100644 index 000000000..2b3a2020f --- /dev/null +++ b/docs/search-ui-react.geolocationprops.geolocationoptions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [geolocationOptions](./search-ui-react.geolocationprops.geolocationoptions.md) + +## GeolocationProps.geolocationOptions property + +Configuration used when collecting the user's location. Definition: [https://w3c.github.io/geolocation-api/\#position\_options\_interface](https://w3c.github.io/geolocation-api/#position_options_interface). + +Signature: + +```typescript +geolocationOptions?: PositionOptions; +``` diff --git a/docs/search-ui-react.geolocationprops.handleclick.md b/docs/search-ui-react.geolocationprops.handleclick.md new file mode 100644 index 000000000..5e86891a8 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.handleclick.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [handleClick](./search-ui-react.geolocationprops.handleclick.md) + +## GeolocationProps.handleClick property + +A function which is called when the geolocation button is clicked, after user's position is successfully determined. + +Signature: + +```typescript +handleClick?: (position: GeolocationPosition) => void; +``` diff --git a/docs/search-ui-react.geolocationprops.label.md b/docs/search-ui-react.geolocationprops.label.md new file mode 100644 index 000000000..128d01079 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.label.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [label](./search-ui-react.geolocationprops.label.md) + +## GeolocationProps.label property + +The label for the button. Defaults to 'Use my location'. + +Signature: + +```typescript +label?: string; +``` diff --git a/docs/search-ui-react.geolocationprops.md b/docs/search-ui-react.geolocationprops.md new file mode 100644 index 000000000..565771b33 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) + +## GeolocationProps interface + +The props for the Geolocation component. + +Signature: + +```typescript +export interface GeolocationProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [customCssClasses?](./search-ui-react.geolocationprops.customcssclasses.md) | [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) | (Optional) CSS classes for customizing the component styling. | +| [GeolocationIcon?](./search-ui-react.geolocationprops.geolocationicon.md) | React.FunctionComponent | (Optional) Custom icon component to display along with the button. | +| [geolocationOptions?](./search-ui-react.geolocationprops.geolocationoptions.md) | PositionOptions | (Optional) Configuration used when collecting the user's location. Definition: [https://w3c.github.io/geolocation-api/\#position\_options\_interface](https://w3c.github.io/geolocation-api/#position_options_interface). | +| [handleClick?](./search-ui-react.geolocationprops.handleclick.md) | (position: GeolocationPosition) => void | (Optional) A function which is called when the geolocation button is clicked, after user's position is successfully determined. | +| [label?](./search-ui-react.geolocationprops.label.md) | string | (Optional) The label for the button. Defaults to 'Use my location'. | +| [radius?](./search-ui-react.geolocationprops.radius.md) | number | (Optional) The radius, in miles, around the user's location to find results. Defaults to 50. If location accuracy is low, a larger radius may be used automatically. | + diff --git a/docs/search-ui-react.geolocationprops.radius.md b/docs/search-ui-react.geolocationprops.radius.md new file mode 100644 index 000000000..62d171e06 --- /dev/null +++ b/docs/search-ui-react.geolocationprops.radius.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GeolocationProps](./search-ui-react.geolocationprops.md) > [radius](./search-ui-react.geolocationprops.radius.md) + +## GeolocationProps.radius property + +The radius, in miles, around the user's location to find results. Defaults to 50. If location accuracy is low, a larger radius may be used automatically. + +Signature: + +```typescript +radius?: number; +``` diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index b5da0d550..02b5ae832 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -18,6 +18,7 @@ | [executeSearch(searchActions)](./search-ui-react.executesearch.md) | Executes a universal/vertical search. | | [FilterDivider({ className })](./search-ui-react.filterdivider.md) | A divider component used to separate NumericalFacets, HierarchicalFacets, StandardFacets, and StaticFilters. | | [FilterSearch({ searchFields, label, placeholder, searchOnSelect, onSelect, sectioned, customCssClasses })](./search-ui-react.filtersearch.md) | A component which allows a user to search for filters associated with specific entities and fields. | +| [Geolocation\_2({ geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, })](./search-ui-react.geolocation_2.md) | A React Component which collects location information to create a location filter and perform a new search. | | [getSearchIntents(searchActions)](./search-ui-react.getsearchintents.md) | Get search intents of the current query stored in headless using autocomplete request. | | [getUserLocation(geolocationOptions)](./search-ui-react.getuserlocation.md) | Retrieves user's location using navigator.geolocation API. | | [HierarchicalFacets({ searchOnChange, collapsible, defaultExpanded, includedFieldIds, customCssClasses, delimiter, showMoreLimit })](./search-ui-react.hierarchicalfacets.md) | A component that displays hierarchical facets, in a tree level structure, applicable to the current vertical search. | @@ -64,6 +65,8 @@ | [FilterOptionConfig](./search-ui-react.filteroptionconfig.md) | The configuration data for a field value filter option. | | [FilterSearchCssClasses](./search-ui-react.filtersearchcssclasses.md) | The CSS class interface for [FilterSearch()](./search-ui-react.filtersearch.md). | | [FilterSearchProps](./search-ui-react.filtersearchprops.md) | The props for the [FilterSearch()](./search-ui-react.filtersearch.md) component. | +| [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) | The CSS class interface for the Geolocation component. | +| [GeolocationProps](./search-ui-react.geolocationprops.md) | The props for the Geolocation component. | | [HierarchicalFacetDisplayCssClasses](./search-ui-react.hierarchicalfacetdisplaycssclasses.md) | The CSS class interface for HierarchicalFacetDisplay. | | [HierarchicalFacetsCssClasses](./search-ui-react.hierarchicalfacetscssclasses.md) | The CSS class interface for [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md). | | [HierarchicalFacetsProps](./search-ui-react.hierarchicalfacetsprops.md) | Props for the [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) component. | diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index 766c23385..a3498a20e 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -279,6 +279,30 @@ export interface FilterSearchProps { // @public export type FocusedItemData = Record; +// @public +function Geolocation_2({ geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, }: GeolocationProps): JSX.Element | null; +export { Geolocation_2 as Geolocation } + +// @public +export interface GeolocationCssClasses { + // (undocumented) + button?: string; + // (undocumented) + geolocationContainer?: string; + // (undocumented) + iconContainer?: string; +} + +// @public +export interface GeolocationProps { + customCssClasses?: GeolocationCssClasses; + GeolocationIcon?: React.FunctionComponent; + geolocationOptions?: PositionOptions; + handleClick?: (position: GeolocationPosition) => void; + label?: string; + radius?: number; +} + // @public export function getSearchIntents(searchActions: SearchActions): Promise; diff --git a/src/components/AppliedFilters.tsx b/src/components/AppliedFilters.tsx index 26f20d6d4..311f66475 100644 --- a/src/components/AppliedFilters.tsx +++ b/src/components/AppliedFilters.tsx @@ -45,7 +45,7 @@ export interface AppliedFiltersProps { customCssClasses?: AppliedFiltersCssClasses } -const DEFUALT_HIDDEN_FIELDS = ['builtin.entityType']; +const DEFAULT_HIDDEN_FIELDS = ['builtin.entityType']; /** * A component that displays a list of filters applied to the current vertical @@ -61,7 +61,7 @@ export function AppliedFilters(props: AppliedFiltersProps): JSX.Element { const isLoading = useSearchState(state => state.searchStatus.isLoading); const { - hiddenFields = DEFUALT_HIDDEN_FIELDS, + hiddenFields = DEFAULT_HIDDEN_FIELDS, customCssClasses = {}, hierarchicalFacetsDelimiter = DEFAULT_HIERARCHICAL_DELIMITER, hierarchicalFacetsFieldIds diff --git a/src/components/Geolocation.tsx b/src/components/Geolocation.tsx new file mode 100644 index 000000000..e7d425ef6 --- /dev/null +++ b/src/components/Geolocation.tsx @@ -0,0 +1,87 @@ +import { useComposedCssClasses } from '../hooks/useComposedCssClasses'; +import LoadingIndicator from '../icons/LoadingIndicator'; +import { YextIcon } from '../icons/YextIcon'; +import { useGeolocationHandler } from '../hooks/useGeolocationHandler'; + +/** + * The CSS class interface for the Geolocation component. + * + * @public + */ +export interface GeolocationCssClasses { + geolocationContainer?: string, + button?: string, + iconContainer?: string +} + +const builtInCssClasses: Readonly = { + geolocationContainer: 'text-sm text-neutral text-center justify-center items-center flex flex-row', + button: 'text-primary font-semibold hover:underline focus:underline', + iconContainer: 'w-4 ml-2' +}; + +/** + * The props for the Geolocation component. + * + * @public + */ +export interface GeolocationProps { + /** + * Configuration used when collecting the user's location. + * Definition: {@link https://w3c.github.io/geolocation-api/#position_options_interface}. + */ + geolocationOptions?: PositionOptions, + /** + * The radius, in miles, around the user's location to find results. Defaults to 50. + * If location accuracy is low, a larger radius may be used automatically. + */ + radius?: number, + /** The label for the button. Defaults to 'Use my location'. */ + label?: string, + /** Custom icon component to display along with the button. */ + GeolocationIcon?: React.FunctionComponent, + /** + * A function which is called when the geolocation button is clicked, + * after user's position is successfully determined. + */ + handleClick?: (position: GeolocationPosition) => void, + /** CSS classes for customizing the component styling. */ + customCssClasses?: GeolocationCssClasses +} + +/** + * A React Component which collects location information to create a + * location filter and perform a new search. + * + * @public + * + * @param props - {@link GeolocationProps} + * @returns A react component for geolocation + */ +export function Geolocation({ + geolocationOptions, + radius = 50, + label = 'Use my location', + //TODO: replace default icon with SVG create from design team + GeolocationIcon = YextIcon, + handleClick, + customCssClasses, +}: GeolocationProps): JSX.Element | null { + const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses); + const [handleGeolocationClick, isFetchingUserLocation] = useGeolocationHandler({ + geolocationOptions, + radius, + handleUserPosition: handleClick + }); + + return ( +
+ +
+ {isFetchingUserLocation ? : } +
+
+ ); +} diff --git a/src/components/index.ts b/src/components/index.ts index 44dd1e1a5..3ad4b3992 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -45,6 +45,12 @@ export { LocationBiasProps } from './LocationBias'; +export { + Geolocation, + GeolocationCssClasses, + GeolocationProps +} from './Geolocation'; + export { AppliedFilters, AppliedFiltersCssClasses, diff --git a/src/hooks/useGeolocationHandler.ts b/src/hooks/useGeolocationHandler.ts new file mode 100644 index 000000000..a2d4ec495 --- /dev/null +++ b/src/hooks/useGeolocationHandler.ts @@ -0,0 +1,81 @@ +import { Matcher, SelectableStaticFilter, useSearchActions, useSearchState } from '@yext/search-headless-react'; +import { executeSearch } from '../utils/search-operations'; +import { getUserLocation } from '../utils/location-operations'; +import { useCallback, useState } from 'react'; + +const LOCATION_FIELD_ID = 'builtin.location'; +const METERS_PER_MILE = 1609.344; + +/** + * The props for {@link useGeolocationHandler} hook. + * + * @internal + */ +interface GeolocationHandlerArgs { + /** Configuration used when collecting the user's location. */ + geolocationOptions?: PositionOptions, + /** + * The radius, in miles, around the user's location to find results. Defaults to 50. + * If location accuracy is low, a larger radius may be used automatically. + */ + radius?: number, + /** Custom handler function to call after user's position is successfully determined. */ + handleUserPosition?: (position: GeolocationPosition) => void +} + +/** + * Creates a function to collect user's geolocation and, by default, will set + * a built-in location filter and execute a search. + * + * @internal + * + * @param props - {@link GeolocationHandlerArgs} + * @returns - A function to collect and process user's geolocation + * - A boolean to indicate if user's geolocation is being fetch + */ +export function useGeolocationHandler({ + geolocationOptions, + radius = 50, + handleUserPosition +}: GeolocationHandlerArgs): [() => Promise, boolean] { + const [isFetchingUserLocation, setIsFetchingUserLocation] = useState(false); + const searchActions = useSearchActions(); + const staticFilters = useSearchState(s => s.filters.static || []); + + const defaultHandleUserPosition = useCallback((position: GeolocationPosition) => { + const { latitude, longitude, accuracy } = position.coords; + const locationFilter: SelectableStaticFilter = { + displayName: 'Current Location', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: LOCATION_FIELD_ID, + matcher: Matcher.Near, + value: { + lat: latitude, + lng: longitude, + radius: Math.max(accuracy, radius * METERS_PER_MILE) + }, + } + }; + const nonLocationFilters = staticFilters.filter(filter => { + return !(filter.filter.kind === 'fieldValue' + && filter.filter.fieldId === LOCATION_FIELD_ID); + }); + searchActions.setStaticFilters([...nonLocationFilters, locationFilter]); + executeSearch(searchActions); + }, [radius, searchActions, staticFilters]); + + const geolocationHandler = useCallback(async () => { + setIsFetchingUserLocation(true); + try { + const position = await getUserLocation(geolocationOptions); + (handleUserPosition ?? defaultHandleUserPosition)(position); + } catch (e) { + console.warn(e); + } finally { + setIsFetchingUserLocation(false); + } + }, [setIsFetchingUserLocation, geolocationOptions, handleUserPosition, defaultHandleUserPosition]); + return [geolocationHandler, isFetchingUserLocation]; +} diff --git a/test-site/src/pages/ProductsPage.tsx b/test-site/src/pages/ProductsPage.tsx index 5bc2af79c..52c6a0640 100644 --- a/test-site/src/pages/ProductsPage.tsx +++ b/test-site/src/pages/ProductsPage.tsx @@ -5,7 +5,7 @@ import { SearchBar, StandardCard, VerticalResults, - LocationBias, + Geolocation, NumericalFacets, Pagination } from '@yext/search-ui-react'; @@ -34,7 +34,7 @@ export function ProductsPage() { CardComponent={StandardCard} /> - + diff --git a/tests/components/Geolocation.stories.tsx b/tests/components/Geolocation.stories.tsx new file mode 100644 index 000000000..e74cc8614 --- /dev/null +++ b/tests/components/Geolocation.stories.tsx @@ -0,0 +1,45 @@ +import { ComponentMeta, Story } from '@storybook/react'; +import { SearchHeadlessContext } from '@yext/search-headless-react'; + +import { decorator as LocationOperationDecorator } from '../__fixtures__/utils/location-operations'; +import { generateMockedHeadless } from '../__fixtures__/search-headless'; +import { VerticalSearcherState } from '../__fixtures__/headless-state'; +import { userEvent, within } from '@storybook/testing-library'; +import { Geolocation, GeolocationProps } from '../../src/components/Geolocation'; + +const meta: ComponentMeta = { + title: 'Geolocation', + component: Geolocation, + argTypes: { + geolocationOptions: { + control: false + }, + GeolocationIcon: { + control: false + }, + handleClick: { + control: false + }, + } +}; +export default meta; + +export const Primary: Story = (args) => { + return ( + + + + ); +}; + +export const Loading = Primary.bind({}); +Loading.decorators = [LocationOperationDecorator]; +Loading.parameters = { + geoLocation: { + isFetching: true + } +}; +Loading.play = ({ canvasElement }) => { + const canvas = within(canvasElement); + userEvent.click(canvas.getByText('Use my location')); +}; diff --git a/tests/components/Geolocation.test.tsx b/tests/components/Geolocation.test.tsx new file mode 100644 index 000000000..2a6b57568 --- /dev/null +++ b/tests/components/Geolocation.test.tsx @@ -0,0 +1,246 @@ +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Geolocation } from '../../src/components/Geolocation'; +import { Matcher, SelectableStaticFilter, State } from '@yext/search-headless-react'; +import * as locationOperations from '../../src/utils/location-operations'; +import { mockAnswersHooks, mockAnswersState, spyOnActions } from '../__utils__/mocks'; + +jest.mock('@yext/search-headless-react'); + +const mockedState: Partial = { + filters: { + static: [] + }, + vertical: { + verticalKey: 'jobs', + }, + searchStatus: { + isLoading: false + }, + meta: { + searchType: 'vertical' + } +}; + +const mockedStateWithFilters: Partial = { + ...mockedState, + filters: { + static: [ + { + displayName: 'Some Location', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'builtin.location', + matcher: Matcher.Near, + value: { + lat: 1, + lng: 1, + radius: 10 + }, + } + }, + { + displayName: 'Current Location', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'builtin.location', + matcher: Matcher.Near, + value: { + lat: 2, + lng: 3, + radius: 10 + }, + } + }, + { + displayName: 'My name', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'employeeName', + matcher: Matcher.Equals, + value: 'Bob', + } + } + ] + } +}; + +const newGeoPosition: GeolocationPosition = { + coords: { + accuracy: 0, + altitude: null, + altitudeAccuracy: null, + heading: null, + latitude: 40.741591687843005, + longitude: -74.00530254443494, + speed: null, + }, + timestamp: 0 +}; + +const newGeoPositionWithLowAccuracy: GeolocationPosition = { + coords: { + ...newGeoPosition.coords, + accuracy: 100000, + }, + timestamp: 0 +}; + +beforeEach(() => { + mockAnswersHooks({ + mockedState, + mockedActions: { + state: mockedState, + setStaticFilters: jest.fn(), + executeVerticalQuery: jest.fn() + } + }); + jest.spyOn(locationOperations, 'getUserLocation').mockResolvedValue(newGeoPosition); +}); + +it('renders custom label when provided', () => { + render(); + const updateLocationButton = screen.getByRole('button', { name: 'Click me!' }); + expect(updateLocationButton).toBeDefined(); +}); + +it('renders custom icon when provided', () => { + render( Custom Icon} />); + const LocationIcon = screen.getByAltText('Custom Icon'); + expect(LocationIcon).toBeDefined(); +}); + +describe('custom click handler', () => { + it('executes handleClick when user\'s location is successfully determined', async () => { + const mockedHandleClickFn = jest.fn(); + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + await waitFor(() => { + expect(mockedHandleClickFn).toHaveBeenCalledWith(newGeoPosition); + }); + expect(actions.executeVerticalQuery).not.toBeCalled(); + }); + + it('does not execute handleClick when error occurs from collecting user\'s location', async () => { + const consoleWarnSpy = jest.spyOn(global.console, 'warn').mockImplementation(); + jest.spyOn(locationOperations, 'getUserLocation').mockRejectedValue('mocked error!'); + const mockedHandleClickFn = jest.fn(); + render(); + clickUpdateLocation(); + await waitFor(() => { + expect(consoleWarnSpy).toBeCalledWith('mocked error!'); + }); + expect(mockedHandleClickFn).not.toBeCalled(); + }); +}); + +describe('default click handler', () => { + it('sets a location filter using provided radius', async () => { + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + + const expectedLocationFilter: SelectableStaticFilter = createLocationFilter(10 * 1609.344); + await waitFor(() => { + expect(actions.setStaticFilters).toBeCalledWith([expectedLocationFilter]); + }); + }); + + it('sets a location filter with user\'s coordinates in static filters state when clicked', async () => { + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + + const expectedLocationFilter: SelectableStaticFilter = createLocationFilter(); + expect(locationOperations.getUserLocation).toBeCalled(); + await waitFor(() => { + expect(actions.setStaticFilters).toBeCalledWith([expectedLocationFilter]); + }); + }); + + it('replace existing location filters with a new location filter in static filters state', async () => { + mockAnswersState(mockedStateWithFilters); + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + + const expectedStaticFilters = [ + { + displayName: 'My name', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'employeeName', + matcher: Matcher.Equals, + value: 'Bob', + } + }, + createLocationFilter() + ]; + await waitFor(() => { + expect(actions.setStaticFilters).toBeCalledWith(expectedStaticFilters); + }); + }); + + it('sets a location filter using a larger radius than provided value due to low accuracy of user coordinate', async () => { + jest.spyOn(locationOperations, 'getUserLocation').mockResolvedValue(newGeoPositionWithLowAccuracy); + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + + const accuracy = newGeoPositionWithLowAccuracy.coords.accuracy; + const expectedLocationFilter: SelectableStaticFilter = createLocationFilter(accuracy); + await waitFor(() => { + expect(actions.setStaticFilters).toBeCalledWith([expectedLocationFilter]); + }); + }); + + it('executes a new search when clicked', async () => { + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + + await waitFor(() => { + expect(actions.executeVerticalQuery).toBeCalled(); + }); + }); + + it('does not execute default handleClick when error occurs from collecting user\'s location', async () => { + const consoleWarnSpy = jest.spyOn(global.console, 'warn').mockImplementation(); + jest.spyOn(locationOperations, 'getUserLocation').mockRejectedValue('mocked error!'); + const actions = spyOnActions(); + render(); + clickUpdateLocation(); + await waitFor(() => { + expect(consoleWarnSpy).toBeCalledWith('mocked error!'); + }); + expect(actions.setStaticFilters).not.toBeCalled(); + expect(actions.executeVerticalQuery).not.toBeCalled(); + }); +}); + +function clickUpdateLocation() { + const updateLocationButton = screen.getByRole('button'); + userEvent.click(updateLocationButton); +} + +function createLocationFilter(radius: number = 50 * 1609.344): SelectableStaticFilter { + return { + displayName: 'Current Location', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'builtin.location', + matcher: Matcher.Near, + value: { + lat: newGeoPosition.coords.latitude, + lng: newGeoPosition.coords.longitude, + radius + }, + } + }; +} \ No newline at end of file From 2ce727e696caab5f7d2a8ed0d605ad303bbafafb Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Tue, 6 Dec 2022 09:52:01 -0500 Subject: [PATCH 05/23] Update Geolocation to remove all location fields (#344) Update `useGeolocationHandler` so that it replaces all location filters with the new filter. In other words, filters on the `builtin.location`, `builtin.region`, and `address.countryCode` fields are all removed when a new filter is selected via the `Geolocation` component. This matches the behavior of `FilterSearch` and ensures the `State` doesn't end up with multiple location filters applied at the same time, which can have unpredictable results from the backend. J=SLAP-2495 TEST=auto See that the Jest tests still pass even when the mocked state includes other kinds of location filters besides those on `builtin.location`. --- src/hooks/useGeolocationHandler.ts | 7 ++++--- tests/components/Geolocation.test.tsx | 28 +++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/hooks/useGeolocationHandler.ts b/src/hooks/useGeolocationHandler.ts index a2d4ec495..457f20274 100644 --- a/src/hooks/useGeolocationHandler.ts +++ b/src/hooks/useGeolocationHandler.ts @@ -3,7 +3,8 @@ import { executeSearch } from '../utils/search-operations'; import { getUserLocation } from '../utils/location-operations'; import { useCallback, useState } from 'react'; -const LOCATION_FIELD_ID = 'builtin.location'; +const GEOLOCATION_FIELD_ID = 'builtin.location'; +const LOCATION_FIELD_IDS = [GEOLOCATION_FIELD_ID, 'builtin.region', 'address.countryCode']; const METERS_PER_MILE = 1609.344; /** @@ -49,7 +50,7 @@ export function useGeolocationHandler({ selected: true, filter: { kind: 'fieldValue', - fieldId: LOCATION_FIELD_ID, + fieldId: GEOLOCATION_FIELD_ID, matcher: Matcher.Near, value: { lat: latitude, @@ -60,7 +61,7 @@ export function useGeolocationHandler({ }; const nonLocationFilters = staticFilters.filter(filter => { return !(filter.filter.kind === 'fieldValue' - && filter.filter.fieldId === LOCATION_FIELD_ID); + && LOCATION_FIELD_IDS.includes(filter.filter.fieldId)); }); searchActions.setStaticFilters([...nonLocationFilters, locationFilter]); executeSearch(searchActions); diff --git a/tests/components/Geolocation.test.tsx b/tests/components/Geolocation.test.tsx index 2a6b57568..d5139a2f7 100644 --- a/tests/components/Geolocation.test.tsx +++ b/tests/components/Geolocation.test.tsx @@ -54,6 +54,26 @@ const mockedStateWithFilters: Partial = { }, } }, + { + displayName: 'Virginia, US', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'builtin.region', + matcher: Matcher.Equals, + value: 'US-VA', + } + }, + { + displayName: 'United States', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'address.countryCode', + matcher: Matcher.Equals, + value: 'US', + } + }, { displayName: 'My name', selected: true, @@ -141,7 +161,7 @@ describe('custom click handler', () => { describe('default click handler', () => { it('sets a location filter using provided radius', async () => { const actions = spyOnActions(); - render(); + render(); clickUpdateLocation(); const expectedLocationFilter: SelectableStaticFilter = createLocationFilter(10 * 1609.344); @@ -162,7 +182,7 @@ describe('default click handler', () => { }); }); - it('replace existing location filters with a new location filter in static filters state', async () => { + it('replaces existing location filters with a new location filter in static filters state', async () => { mockAnswersState(mockedStateWithFilters); const actions = spyOnActions(); render(); @@ -189,7 +209,7 @@ describe('default click handler', () => { it('sets a location filter using a larger radius than provided value due to low accuracy of user coordinate', async () => { jest.spyOn(locationOperations, 'getUserLocation').mockResolvedValue(newGeoPositionWithLowAccuracy); const actions = spyOnActions(); - render(); + render(); clickUpdateLocation(); const accuracy = newGeoPositionWithLowAccuracy.coords.accuracy; @@ -243,4 +263,4 @@ function createLocationFilter(radius: number = 50 * 1609.344): SelectableStaticF }, } }; -} \ No newline at end of file +} From cc65e8b0f98bba9af5e7b75b32bda8f2ad544d9e Mon Sep 17 00:00:00 2001 From: Tristan Timblin Date: Mon, 12 Dec 2022 09:35:34 -0500 Subject: [PATCH 06/23] add checks to fail combined-coverage tests after each part (#346) Currently tests are failing silently in Github actions. This PR adds checks to combined-coverage.sh script to exit if a test fails. The three tests currently failing on main will now be caught, I created this item to handle them separately, as it will require a change to make react versions to test programmable in reusable workflows J=SLAP-2459 TEST=manual ran actions with an intentional failure (log). Also commented out npm publish from the slap publish command and tested that it makes it passed the check there --- package.json | 5 +++-- tests/scripts/combined-coverage.sh | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0401a0c25..2b287aafd 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", "wcag": "test-storybook", - "test:unit": "jest --coverage --coverageDirectory=coverage/unit", + "test:unit": "jest", "test:visual": "./tests/scripts/visual-coverage.sh", "test": "./tests/scripts/combined-coverage.sh" }, @@ -100,11 +100,12 @@ "jest": { "bail": 0, "verbose": true, - "collectCoverage": false, + "collectCoverage": true, "collectCoverageFrom": [ "src/**", "!src/models/**" ], + "coverageDirectory": "coverage/unit", "moduleFileExtensions": [ "js", "ts", diff --git a/tests/scripts/combined-coverage.sh b/tests/scripts/combined-coverage.sh index 58bdfa34d..e35e3b236 100755 --- a/tests/scripts/combined-coverage.sh +++ b/tests/scripts/combined-coverage.sh @@ -1,7 +1,7 @@ #!/bin/bash -npm run test:unit -npm run test:visual +npm run test:unit || exit 1 +npm run test:visual || exit 1 # merge mkdir -p coverage/merge From 3aeb9beca2812faee7f76d6cbabf3e1b21e7f25d Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Wed, 21 Dec 2022 15:57:13 -0500 Subject: [PATCH 07/23] Use reusable workflow in sync_sites_branch (#349) Update the `sync_sites_branch` workflow to use the new reusable workflow. J=SLAP-2507 TEST=manual Test on a forked repo and see that a commit to `main` triggers a merge commit to the `storybook-site` and `test-site` branches. --- .github/workflows/sync-sites-branch.yml | 27 +++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/.github/workflows/sync-sites-branch.yml b/.github/workflows/sync-sites-branch.yml index dbee82df6..5ceccf24f 100644 --- a/.github/workflows/sync-sites-branch.yml +++ b/.github/workflows/sync-sites-branch.yml @@ -6,21 +6,12 @@ on: - main jobs: - sync-branches: - runs-on: ubuntu-latest - name: Syncing branches - steps: - - name: Checkout - uses: actions/checkout@v3 - - uses: devmasx/merge-branch@v1.4.0 - with: - type: now - from_branch: ${{ github.event.repository.default_branch }} - target_branch: storybook-site - github_token: ${{ secrets.GITHUB_TOKEN }} - - uses: devmasx/merge-branch@v1.4.0 - with: - type: now - from_branch: ${{ github.event.repository.default_branch }} - target_branch: test-site - github_token: ${{ secrets.GITHUB_TOKEN }} + call_sync_branches: + strategy: + matrix: + target_branch: ["storybook-site", "test-site"] + uses: yext/slapshot-reusable-workflows/.github/workflows/sync_default_branch.yml@v1 + with: + target_branch: ${{ matrix.target_branch }} + secrets: + caller_github_token: ${{ secrets.GITHUB_TOKEN }} From cb546c5a0c3eecd56731623df38c560288effeb3 Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Wed, 4 Jan 2023 15:37:42 -0500 Subject: [PATCH 08/23] Pin react-collapsed to minor version (#350) This PR pins `react-collapsed` to the latest minor version (v3.6.0) that supports React 17. In v3.7.0 and v3.8.0, `react-collapsed` fixed a bug ([Slack thread](https://yext.slack.com/archives/C032CKFARGS/p1671130163844249), [GH issue](https://github.com/roginfarrer/react-collapsed/issues/103)) that occurs when using it with React 18 by introducing a breaking change where they dropped compatibility with React 16 and 17. v3.7.0+ would break a search experience using collapsible facets with React <18. Since they are not able to support both React 17 and 18 together, it seems preferable to pin to a version that can be used with both, but is a little buggy with React 18, rather than fully functional with React 18, but breaks other React experiences. J=SLAP-2521 TEST=manual See that collapsible facets works as expected on a test-site running on React 17 and that the site running on React 18 still has most functionality, except for the bug described above. --- THIRD-PARTY-NOTICES | 34 +---------------------------- package-lock.json | 43 ++++++++----------------------------- package.json | 2 +- test-site/package-lock.json | 8 ++++--- 4 files changed, 16 insertions(+), 71 deletions(-) diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index e2dc70f1e..66e8a7fca 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -3520,22 +3520,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------- -The following NPM package may be included in this product: - - - performance-now@2.1.0 - -This package contains the following license and notice below: - -Copyright (c) 2013 Braveg1rl - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------------ - The following NPM package may be included in this product: - picocolors@1.0.0 @@ -3883,23 +3867,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following NPM package may be included in this product: - - raf@3.4.1 - -This package contains the following license and notice below: - -Copyright 2013 Chris Dickinson - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------------ - -The following NPM package may be included in this product: - - - react-collapsed@3.3.0 + - react-collapsed@3.6.0 This package contains the following license and notice below: diff --git a/package-lock.json b/package-lock.json index f08201330..56e91d1d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", "prop-types": "^15.8.1", - "react-collapsed": "^3.3.0", + "react-collapsed": "~3.6.0", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", "use-isomorphic-layout-effect": "^1.1.2" @@ -27015,10 +27015,6 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, - "node_modules/performance-now": { - "version": "2.1.0", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "node_modules/picocolors": { "version": "1.0.0", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" @@ -27823,13 +27819,6 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, - "node_modules/raf": { - "version": "3.4.1", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dependencies": { - "performance-now": "^2.1.0" - } - }, "node_modules/ramda": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", @@ -27922,18 +27911,15 @@ } }, "node_modules/react-collapsed": { - "version": "3.3.0", - "integrity": "sha512-2oXo9xsleo3ZwmNP7GymWbkZp2SwDZImLduy1LKgQMsqZTDldyzP2GnfvYb9vyGFDYHRYFhnWlwDmG2yWkt8eQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-3.6.0.tgz", + "integrity": "sha512-QqtogOGl5hM9L7j7rlMCYxm4jD8Ovr8voqyYS1g5ltADhUNvxbbgtJ5MwRiajJ0DmYFOZHShpnSPz4wvJaOiKA==", "dependencies": { - "raf": "^3.4.1", "tiny-warning": "^1.0.3" }, - "engines": { - "node": ">=12" - }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": "^16.8 || ^17", + "react-dom": "^16.8 || ^17" } }, "node_modules/react-docgen": { @@ -53697,10 +53683,6 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, - "performance-now": { - "version": "2.1.0", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picocolors": { "version": "1.0.0", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" @@ -54279,13 +54261,6 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, - "raf": { - "version": "3.4.1", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "requires": { - "performance-now": "^2.1.0" - } - }, "ramda": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", @@ -54354,10 +54329,10 @@ } }, "react-collapsed": { - "version": "3.3.0", - "integrity": "sha512-2oXo9xsleo3ZwmNP7GymWbkZp2SwDZImLduy1LKgQMsqZTDldyzP2GnfvYb9vyGFDYHRYFhnWlwDmG2yWkt8eQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/react-collapsed/-/react-collapsed-3.6.0.tgz", + "integrity": "sha512-QqtogOGl5hM9L7j7rlMCYxm4jD8Ovr8voqyYS1g5ltADhUNvxbbgtJ5MwRiajJ0DmYFOZHShpnSPz4wvJaOiKA==", "requires": { - "raf": "^3.4.1", "tiny-warning": "^1.0.3" } }, diff --git a/package.json b/package.json index 69e1ed479..96379110a 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", "prop-types": "^15.8.1", - "react-collapsed": "^3.3.0", + "react-collapsed": "~3.6.0", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", "use-isomorphic-layout-effect": "^1.1.2" diff --git a/test-site/package-lock.json b/test-site/package-lock.json index e69cad304..762030b58 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,7 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", - "version": "1.1.0-beta.335", + "version": "1.1.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -45,7 +45,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", "prop-types": "^15.8.1", - "react-collapsed": "^3.3.0", + "react-collapsed": "~3.6.0", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", "use-isomorphic-layout-effect": "^1.1.2" @@ -55,6 +55,7 @@ "@babel/preset-env": "^7.14.7", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.14.5", + "@etchteam/storybook-addon-status": "^4.2.2", "@percy/cli": "^1.8.0", "@percy/storybook": "^4.3.3", "@reduxjs/toolkit": "^1.8.6", @@ -20080,6 +20081,7 @@ "@babel/preset-env": "^7.14.7", "@babel/preset-react": "^7.16.7", "@babel/preset-typescript": "^7.14.5", + "@etchteam/storybook-addon-status": "^4.2.2", "@microsoft/api-documenter": "^7.15.3", "@microsoft/api-extractor": "^7.19.4", "@percy/cli": "^1.8.0", @@ -20126,7 +20128,7 @@ "msw": "^0.36.8", "prop-types": "^15.8.1", "react": "^17.0.2", - "react-collapsed": "^3.3.0", + "react-collapsed": "~3.6.0", "react-dom": "^17.0.2", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", From 0583f8e85469b4222cbcbcb1f45b50a3b894c5fb Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Mon, 10 Apr 2023 13:32:37 -0400 Subject: [PATCH 09/23] Add compatibility notes to README (#358) Update the README to add notes about compatibility with Webpack in response to [this](https://github.com/yext/search-ui-react/issues/357) issue. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 680e377fe..d5b509ac5 100644 --- a/README.md +++ b/README.md @@ -78,4 +78,8 @@ To use the Component Library's Styling without adding Tailwind to your project, ```tsx import '@yext/search-ui-react/bundle.css' -``` \ No newline at end of file +``` + +## Compatibility Notes + +This library and its dependencies use optional chaining and other modern TS syntax that is not inherently supported by Webpack <5 (e.g. via `create-react-app@4`). Additional Babel plugins are needed for transpiling if using legacy versions. \ No newline at end of file From bbf904b605030c92a14d76c41e6ff090f48b79b0 Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Mon, 1 May 2023 10:33:55 -0400 Subject: [PATCH 10/23] Create Facets component (#360) Create a facets component which renders any facets returned by the search. If any hierarchical facets are returned, they are rendered as standard facets. J=BACK-2220 TEST=manual, auto Manually tested this component on the test site and confirmed it rendered all types of facets. Also added a storybook page and automated tests and confirmed they passed. --- docs/search-ui-react.facets.md | 30 ++++++ ...-react.facetscssclasses.facetscontainer.md | 11 +++ docs/search-ui-react.facetscssclasses.md | 20 ++++ ...h-ui-react.facetsprops.customcssclasses.md | 13 +++ docs/search-ui-react.facetsprops.md | 20 ++++ docs/search-ui-react.md | 3 + etc/search-ui-react.api.md | 14 +++ src/components/Facets.tsx | 45 +++++++++ src/components/index.ts | 6 ++ test-site/src/pages/PeoplePage.tsx | 2 + tests/__utils__/facets.ts | 5 + tests/components/Facets.stories.tsx | 26 ++++++ tests/components/Facets.test.tsx | 91 +++++++++++++++++++ tests/components/StandardFacets.test.tsx | 9 +- 14 files changed, 289 insertions(+), 6 deletions(-) create mode 100644 docs/search-ui-react.facets.md create mode 100644 docs/search-ui-react.facetscssclasses.facetscontainer.md create mode 100644 docs/search-ui-react.facetscssclasses.md create mode 100644 docs/search-ui-react.facetsprops.customcssclasses.md create mode 100644 docs/search-ui-react.facetsprops.md create mode 100644 src/components/Facets.tsx create mode 100644 tests/__utils__/facets.ts create mode 100644 tests/components/Facets.stories.tsx create mode 100644 tests/components/Facets.test.tsx diff --git a/docs/search-ui-react.facets.md b/docs/search-ui-react.facets.md new file mode 100644 index 000000000..8c8b5b2a6 --- /dev/null +++ b/docs/search-ui-react.facets.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [Facets](./search-ui-react.facets.md) + +## Facets() function + +A component that displays all facets applicable to the current vertical search. + +Signature: + +```typescript +export declare function Facets(props: FacetsProps): JSX.Element; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | [FacetsProps](./search-ui-react.facetsprops.md) | [FacetsProps](./search-ui-react.facetsprops.md) | + +Returns: + +JSX.Element + +A React component for facets + +## Remarks + +This component is a quick way of getting facets on the page, and it will render standard facets, numerical facets, and hierarchical facets. The [StandardFacets()](./search-ui-react.standardfacets.md), [NumericalFacets()](./search-ui-react.numericalfacets.md), and [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) components can be used instead for more control over facet configuration. + diff --git a/docs/search-ui-react.facetscssclasses.facetscontainer.md b/docs/search-ui-react.facetscssclasses.facetscontainer.md new file mode 100644 index 000000000..1e90d3795 --- /dev/null +++ b/docs/search-ui-react.facetscssclasses.facetscontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsCssClasses](./search-ui-react.facetscssclasses.md) > [facetsContainer](./search-ui-react.facetscssclasses.facetscontainer.md) + +## FacetsCssClasses.facetsContainer property + +Signature: + +```typescript +facetsContainer?: string; +``` diff --git a/docs/search-ui-react.facetscssclasses.md b/docs/search-ui-react.facetscssclasses.md new file mode 100644 index 000000000..04c2b7fbf --- /dev/null +++ b/docs/search-ui-react.facetscssclasses.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsCssClasses](./search-ui-react.facetscssclasses.md) + +## FacetsCssClasses interface + +The CSS class interface for [Facets()](./search-ui-react.facets.md). + +Signature: + +```typescript +export interface FacetsCssClasses +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [facetsContainer?](./search-ui-react.facetscssclasses.facetscontainer.md) | string | (Optional) | + diff --git a/docs/search-ui-react.facetsprops.customcssclasses.md b/docs/search-ui-react.facetsprops.customcssclasses.md new file mode 100644 index 000000000..67d9622f6 --- /dev/null +++ b/docs/search-ui-react.facetsprops.customcssclasses.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [customCssClasses](./search-ui-react.facetsprops.customcssclasses.md) + +## FacetsProps.customCssClasses property + +CSS classes for customizing the component styling. + +Signature: + +```typescript +customCssClasses?: FacetsCssClasses; +``` diff --git a/docs/search-ui-react.facetsprops.md b/docs/search-ui-react.facetsprops.md new file mode 100644 index 000000000..7d5cc5f5c --- /dev/null +++ b/docs/search-ui-react.facetsprops.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) + +## FacetsProps interface + +Props for the [Facets()](./search-ui-react.facets.md) component. + +Signature: + +```typescript +export interface FacetsProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [customCssClasses?](./search-ui-react.facetsprops.customcssclasses.md) | [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | (Optional) CSS classes for customizing the component styling. | + diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 02b5ae832..7ddfd54cf 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -16,6 +16,7 @@ | [DropdownItem(\_props)](./search-ui-react.dropdownitem.md) | A wrapper component for specifying a DropdownItemWithIndex. The index will be automatically provided by the Dropdown component instance. | | [executeAutocomplete(searchActions)](./search-ui-react.executeautocomplete.md) | Executes a universal/vertical autocomplete search and return the corresponding response. | | [executeSearch(searchActions)](./search-ui-react.executesearch.md) | Executes a universal/vertical search. | +| [Facets(props)](./search-ui-react.facets.md) | A component that displays all facets applicable to the current vertical search. | | [FilterDivider({ className })](./search-ui-react.filterdivider.md) | A divider component used to separate NumericalFacets, HierarchicalFacets, StandardFacets, and StaticFilters. | | [FilterSearch({ searchFields, label, placeholder, searchOnSelect, onSelect, sectioned, customCssClasses })](./search-ui-react.filtersearch.md) | A component which allows a user to search for filters associated with specific entities and fields. | | [Geolocation\_2({ geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, })](./search-ui-react.geolocation_2.md) | A React Component which collects location information to create a location filter and perform a new search. | @@ -60,6 +61,8 @@ | [CtaData](./search-ui-react.ctadata.md) | The shape of a StandardCard CTA field's data. | | [DirectAnswerCssClasses](./search-ui-react.directanswercssclasses.md) | The CSS class interface for [DirectAnswer()](./search-ui-react.directanswer.md). | | [DirectAnswerProps](./search-ui-react.directanswerprops.md) | Props for [DirectAnswer()](./search-ui-react.directanswer.md). | +| [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | The CSS class interface for [Facets()](./search-ui-react.facets.md). | +| [FacetsProps](./search-ui-react.facetsprops.md) | Props for the [Facets()](./search-ui-react.facets.md) component. | | [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) | The CSS class interface for FilterGroup. | | [FilterGroupProps](./search-ui-react.filtergroupprops.md) | Props for the FilterGroup component. | | [FilterOptionConfig](./search-ui-react.filteroptionconfig.md) | The configuration data for a field value filter option. | diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index a3498a20e..b5d09e778 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -200,6 +200,20 @@ export function executeAutocomplete(searchActions: SearchActions): Promise; +// @public +export function Facets(props: FacetsProps): JSX.Element; + +// @public +export interface FacetsCssClasses { + // (undocumented) + facetsContainer?: string; +} + +// @public +export interface FacetsProps { + customCssClasses?: FacetsCssClasses; +} + // @public export type FeedbackType = 'THUMBS_UP' | 'THUMBS_DOWN'; diff --git a/src/components/Facets.tsx b/src/components/Facets.tsx new file mode 100644 index 000000000..1c5b90c2a --- /dev/null +++ b/src/components/Facets.tsx @@ -0,0 +1,45 @@ +import { StandardFacets } from './StandardFacets'; +import { NumericalFacets } from './NumericalFacets'; + +/** + * The CSS class interface for {@link Facets}. + * + * @public + */ +export interface FacetsCssClasses { + facetsContainer?: string +} + +/** + * Props for the {@link Facets} component. + * + * @public + */ +export interface FacetsProps { + /** CSS classes for customizing the component styling. */ + customCssClasses?: FacetsCssClasses +} + +/** + * A component that displays all facets applicable to the current vertical search. + * + * @remarks + * This component is a quick way of getting facets on the page, and it will render standard facets, + * numerical facets, and hierarchical facets. The {@link StandardFacets}, {@link NumericalFacets}, + * and {@link HierarchicalFacets} components can be used instead for more control over facet + * configuration. + * + * @param props - {@link FacetsProps} + * @returns A React component for facets + * + * @public + */ +export function Facets(props: FacetsProps) { + const { customCssClasses = {} } = props; + return ( +
+ + +
+ ); +} \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts index 3ad4b3992..f12aa093e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -125,6 +125,12 @@ export { NumericalFacetsProps } from './NumericalFacets'; +export { + Facets, + FacetsCssClasses, + FacetsProps +} from './Facets'; + export { FilterGroupProps, FilterGroupCssClasses diff --git a/test-site/src/pages/PeoplePage.tsx b/test-site/src/pages/PeoplePage.tsx index accb81fda..92d783314 100644 --- a/test-site/src/pages/PeoplePage.tsx +++ b/test-site/src/pages/PeoplePage.tsx @@ -55,11 +55,13 @@ export function PeoplePage() { title='Static Employee Department' filterOptions={employeeFilterConfigs} /> + + = { + title: 'Facets', + component: Facets +}; +export default meta; + +const mockedHeadlessState: RecursivePartial = { + filters: { + facets: DisplayableFacets + } +}; + +export const Primary: Story = (args) => { + return ( + + + + ); +}; \ No newline at end of file diff --git a/tests/components/Facets.test.tsx b/tests/components/Facets.test.tsx new file mode 100644 index 000000000..46ca478c8 --- /dev/null +++ b/tests/components/Facets.test.tsx @@ -0,0 +1,91 @@ +import { render, screen } from '@testing-library/react'; +import { + Source, + State +} from '@yext/search-headless-react'; +import { mockAnswersHooks, mockAnswersState } from '../__utils__/mocks'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { Facets } from '../../src'; +import { getOptionLabelText } from '../__utils__/facets'; + +const mockedState: Partial = { + filters: { + static: [], + facets: DisplayableFacets + }, + vertical: { + verticalKey: 'vertical', + results: [{ + source: Source.KnowledgeManager, + rawData: {} + }], + appliedQueryFilters: [] + }, + searchStatus: { + isLoading: false + }, + meta: { + searchType: 'vertical' + } +}; + +const mockedActions = { + state: mockedState, + setOffset: jest.fn(), + setFacetOption: jest.fn(), + executeVerticalQuery: jest.fn() +}; + +const mockedUtils = { + isCloseMatch: () => true +}; + +jest.mock('@yext/search-headless-react'); + +describe('Facets', () => { + beforeEach(() => { + mockAnswersHooks({ mockedState, mockedActions, mockedUtils }); + }); + it('Properly renders standard facets if present', () => { + render(); + const regularFilter = DisplayableFacets[0]; + + expect(screen.getByText(regularFilter.displayName)).toBeDefined(); + regularFilter.options.forEach(o => { + expect(screen.getByText(getOptionLabelText(o))).toBeDefined(); + }); + }); + + it('Properly renders numerical facets if present', () => { + render(); + const numericalFilter = DisplayableFacets[1]; + + expect(screen.getByText(numericalFilter.displayName)).toBeDefined(); + numericalFilter.options.forEach(o => { + expect(screen.getByText(o.displayName)).toBeDefined(); + }); + }); + + it('Does not render filters if no facets are present', () => { + mockAnswersState({ + ...mockedState, + filters: { + static: [], + facets: [] + } + }); + render(); + const regularFilter = DisplayableFacets[0]; + const numericalFilter = DisplayableFacets[1]; + + expect(screen.queryByText(numericalFilter.displayName)).toBeNull(); + numericalFilter.options.forEach(o => { + expect(screen.queryByText(o.displayName)).toBeNull(); + }); + + expect(screen.queryByText(regularFilter.displayName)).toBeNull(); + regularFilter.options.forEach(o => { + expect(screen.queryByText(`${o.displayName} (${o.count})}`)).toBeNull(); + }); + }); +}); \ No newline at end of file diff --git a/tests/components/StandardFacets.test.tsx b/tests/components/StandardFacets.test.tsx index 520dd9dcb..37c64206f 100644 --- a/tests/components/StandardFacets.test.tsx +++ b/tests/components/StandardFacets.test.tsx @@ -1,9 +1,10 @@ import { render, screen } from '@testing-library/react'; -import { DisplayableFacetOption, FacetOption, Source, State, SearchActions } from '@yext/search-headless-react'; +import { FacetOption, Source, State, SearchActions } from '@yext/search-headless-react'; import { mockAnswersHooks, spyOnActions } from '../__utils__/mocks'; import userEvent from '@testing-library/user-event'; import { DisplayableFacets } from '../__fixtures__/data/filters'; import { StandardFacets } from '../../src/components'; +import { getOptionLabelText } from '../__utils__/facets'; const mockedState: Partial = { filters: { @@ -157,8 +158,4 @@ function expectFacetOptionSet( { matcher: option.matcher, value: option.value }, selected ); -} - -function getOptionLabelText(option: DisplayableFacetOption) { - return `${option.displayName} (${option.count})`; -} +} \ No newline at end of file From 96468cd116c8f6f12aea0b9860c5dbc37e3781d8 Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Mon, 8 May 2023 11:06:37 -0400 Subject: [PATCH 11/23] Update @yext/search-headless-react Version (#362) This change updates @yext/search-headless-react to version 2.2.0-beta.0 which contains the EU endpoints change. It also bumps @yext/search-ui-react to version 1.2.0-beta.0. J=BACK-2270 TEST=auto,manual Ran `npm run test`. Ran `npx serve .` under test-site/. --- .gitignore | 2 ++ THIRD-PARTY-NOTICES | 6 ++--- package-lock.json | 48 ++++++++++++++++++------------------ package.json | 6 ++--- src/components/SearchBar.tsx | 7 ++++-- 5 files changed, 37 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index ff07b8e0c..0cfbe0cc3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ lib/ .env test-site/.env + +.idea/ diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 66e8a7fca..e894248c8 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -1020,7 +1020,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-core@2.1.0 + - @yext/search-core@2.3.0-beta.1 This package contains the following license and notice below: @@ -1064,7 +1064,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless-react@2.1.0 + - @yext/search-headless-react@2.2.0-beta.0 This package contains the following license and notice below: @@ -1108,7 +1108,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless@2.1.0 + - @yext/search-headless@2.2.1-beta.0 This package contains the following license and notice below: diff --git a/package-lock.json b/package-lock.json index 56e91d1d3..38c58996a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -71,7 +71,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -10738,9 +10738,9 @@ } }, "node_modules/@yext/search-core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.1.0.tgz", - "integrity": "sha512-zlR3es8AYWKowp+h5uAdvNyyy66xI3FBq6YA5RRF+47J/Zrg9JDM95O+GzkQD1c9yuy4aRDn+NB59f05VQwACQ==", + "version": "2.3.0-beta.1", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0-beta.1.tgz", + "integrity": "sha512-qO8OC88ZftVLVxyylG0CxM/1Jf6xCVpztwDEKnDu/3lKw9Iq/zHZA7E487y1Ja2MraU6WuiMUNFYJpzYRLOcIw==", "dev": true, "dependencies": { "@babel/runtime-corejs3": "^7.12.5", @@ -10751,24 +10751,24 @@ } }, "node_modules/@yext/search-headless": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.1.0.tgz", - "integrity": "sha512-OGjqC9NxSTUCZRdxLEHNpww3QGhypmLTRvjlDxwtVWSsAhWWWMEh1iUmo0fVsdLq7SAmxa5t11V2dgZhk9HMcQ==", + "version": "2.2.1-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.2.1-beta.0.tgz", + "integrity": "sha512-90uvmnE2sOTnv23hTDm4y/DkPsWollufdbNRL43c2RZCsh8GAh2IEWVd1nIdz3k1Ac26XS6kUGVCrGh2TlrWRw==", "dev": true, "dependencies": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.1.0", + "@yext/search-core": "^2.3.0-beta.1", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "node_modules/@yext/search-headless-react": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.1.0.tgz", - "integrity": "sha512-1+alKkKRryyVSYB484Sd8d8VXym+Gd56E7s2JzECaRzWd6ql/JEjloNirzPrLhQuqIH+UhpMg4x6OCcuIp9KvA==", + "version": "2.2.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0-beta.0.tgz", + "integrity": "sha512-fyMHbIWy+xJxZ36YSmBCCFX07fVUboxuVXNIBFn9u7L4DI97daulZE4v0SCtyHm3opgwLvCcx/OggmhLqfbhMQ==", "dev": true, "dependencies": { - "@yext/search-headless": "^2.1.0", + "@yext/search-headless": "^2.2.1-beta.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { @@ -41108,9 +41108,9 @@ } }, "@yext/search-core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.1.0.tgz", - "integrity": "sha512-zlR3es8AYWKowp+h5uAdvNyyy66xI3FBq6YA5RRF+47J/Zrg9JDM95O+GzkQD1c9yuy4aRDn+NB59f05VQwACQ==", + "version": "2.3.0-beta.1", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0-beta.1.tgz", + "integrity": "sha512-qO8OC88ZftVLVxyylG0CxM/1Jf6xCVpztwDEKnDu/3lKw9Iq/zHZA7E487y1Ja2MraU6WuiMUNFYJpzYRLOcIw==", "dev": true, "requires": { "@babel/runtime-corejs3": "^7.12.5", @@ -41118,24 +41118,24 @@ } }, "@yext/search-headless": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.1.0.tgz", - "integrity": "sha512-OGjqC9NxSTUCZRdxLEHNpww3QGhypmLTRvjlDxwtVWSsAhWWWMEh1iUmo0fVsdLq7SAmxa5t11V2dgZhk9HMcQ==", + "version": "2.2.1-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.2.1-beta.0.tgz", + "integrity": "sha512-90uvmnE2sOTnv23hTDm4y/DkPsWollufdbNRL43c2RZCsh8GAh2IEWVd1nIdz3k1Ac26XS6kUGVCrGh2TlrWRw==", "dev": true, "requires": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.1.0", + "@yext/search-core": "^2.3.0-beta.1", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "@yext/search-headless-react": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.1.0.tgz", - "integrity": "sha512-1+alKkKRryyVSYB484Sd8d8VXym+Gd56E7s2JzECaRzWd6ql/JEjloNirzPrLhQuqIH+UhpMg4x6OCcuIp9KvA==", + "version": "2.2.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0-beta.0.tgz", + "integrity": "sha512-fyMHbIWy+xJxZ36YSmBCCFX07fVUboxuVXNIBFn9u7L4DI97daulZE4v0SCtyHm3opgwLvCcx/OggmhLqfbhMQ==", "dev": true, "requires": { - "@yext/search-headless": "^2.1.0", + "@yext/search-headless": "^2.2.1-beta.0", "use-sync-external-store": "^1.1.0" } }, diff --git a/package.json b/package.json index 96379110a..582462000 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.1.0", + "version": "1.2.0-beta.0", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", @@ -76,7 +76,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -93,7 +93,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" }, diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 4f04b1845..12384a43c 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -306,7 +306,7 @@ export function SearchBar({ onClick={handleSubmit} > {renderAutocompleteResult( - { value: result.query }, + { value: result.query, inputIntents: [] }, recentSearchesCssClasses, RecentSearchIcon, `recent search: ${result.query}` @@ -349,7 +349,10 @@ export function SearchBar({ onClick={handleSubmit} > {renderAutocompleteResult( - { value: `in ${verticalKeyToLabel ? verticalKeyToLabel(verticalKey) : verticalKey}` }, + { + value: `in ${verticalKeyToLabel ? verticalKeyToLabel(verticalKey) : verticalKey}`, + inputIntents: [] + }, { ...cssClasses, option: cssClasses.verticalLink } )} From 42456368a6d35d6847813a12fb5d8c5dbb1600ed Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Tue, 9 May 2023 16:19:36 -0400 Subject: [PATCH 12/23] Prepare version 1.2.0 (#365) Bump the search-headless-react version, add hierarchical facet to storybook J=BACK-2237 TEST=manual Ran storybook locally and confirmed the components worked including the new Facets component --- .github/CODEOWNERS | 2 +- .gitignore | 2 +- THIRD-PARTY-NOTICES | 6 ++-- package-lock.json | 52 ++++++++++++++--------------- package.json | 6 ++-- tests/components/Facets.stories.tsx | 11 +++++- 6 files changed, 44 insertions(+), 35 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9712a9afe..eb6e2e991 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @yext/slapshot \ No newline at end of file +* @yext/backfire \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0cfbe0cc3..a0f58dad6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ lib/ .env test-site/.env -.idea/ +.idea/ \ No newline at end of file diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index e894248c8..17221cb2e 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -1020,7 +1020,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-core@2.3.0-beta.1 + - @yext/search-core@2.3.0 This package contains the following license and notice below: @@ -1064,7 +1064,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless-react@2.2.0-beta.0 + - @yext/search-headless-react@2.2.0 This package contains the following license and notice below: @@ -1108,7 +1108,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless@2.2.1-beta.0 + - @yext/search-headless@2.3.0 This package contains the following license and notice below: diff --git a/package-lock.json b/package-lock.json index 38c58996a..2d05180a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-ui-react", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yext/search-ui-react", - "version": "1.1.0", + "version": "1.2.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -54,7 +54,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -71,7 +71,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -10738,9 +10738,9 @@ } }, "node_modules/@yext/search-core": { - "version": "2.3.0-beta.1", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0-beta.1.tgz", - "integrity": "sha512-qO8OC88ZftVLVxyylG0CxM/1Jf6xCVpztwDEKnDu/3lKw9Iq/zHZA7E487y1Ja2MraU6WuiMUNFYJpzYRLOcIw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0.tgz", + "integrity": "sha512-vSvNXWv9E/6s4oRB1og4zHfRTTEHrmUm2sh95Y1Dn94U2mkjNDGSsshEeamU2UIJO7Ee5oT6K6JDU7XAVOxC4A==", "dev": true, "dependencies": { "@babel/runtime-corejs3": "^7.12.5", @@ -10751,24 +10751,24 @@ } }, "node_modules/@yext/search-headless": { - "version": "2.2.1-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.2.1-beta.0.tgz", - "integrity": "sha512-90uvmnE2sOTnv23hTDm4y/DkPsWollufdbNRL43c2RZCsh8GAh2IEWVd1nIdz3k1Ac26XS6kUGVCrGh2TlrWRw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.3.0.tgz", + "integrity": "sha512-Uh5DVeV99dkaeF6ayuUEkcUbI8wHn/7bz4aHrjtdDyl3F/6GX3cyHbs/BQh1kWCr+t8EJUWVPl5s4jmL3tst/Q==", "dev": true, "dependencies": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.3.0-beta.1", + "@yext/search-core": "^2.3.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "node_modules/@yext/search-headless-react": { - "version": "2.2.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0-beta.0.tgz", - "integrity": "sha512-fyMHbIWy+xJxZ36YSmBCCFX07fVUboxuVXNIBFn9u7L4DI97daulZE4v0SCtyHm3opgwLvCcx/OggmhLqfbhMQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0.tgz", + "integrity": "sha512-x2Sx7uS3w5E8RfuIpPsQZGWTGNKS9kRWDlQReUngzxP+UhoBQ8L7qNTGGbJLZ+LUpjRqTS1RuZP4scljbla//g==", "dev": true, "dependencies": { - "@yext/search-headless": "^2.2.1-beta.0", + "@yext/search-headless": "^2.3.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { @@ -41108,9 +41108,9 @@ } }, "@yext/search-core": { - "version": "2.3.0-beta.1", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0-beta.1.tgz", - "integrity": "sha512-qO8OC88ZftVLVxyylG0CxM/1Jf6xCVpztwDEKnDu/3lKw9Iq/zHZA7E487y1Ja2MraU6WuiMUNFYJpzYRLOcIw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0.tgz", + "integrity": "sha512-vSvNXWv9E/6s4oRB1og4zHfRTTEHrmUm2sh95Y1Dn94U2mkjNDGSsshEeamU2UIJO7Ee5oT6K6JDU7XAVOxC4A==", "dev": true, "requires": { "@babel/runtime-corejs3": "^7.12.5", @@ -41118,24 +41118,24 @@ } }, "@yext/search-headless": { - "version": "2.2.1-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.2.1-beta.0.tgz", - "integrity": "sha512-90uvmnE2sOTnv23hTDm4y/DkPsWollufdbNRL43c2RZCsh8GAh2IEWVd1nIdz3k1Ac26XS6kUGVCrGh2TlrWRw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.3.0.tgz", + "integrity": "sha512-Uh5DVeV99dkaeF6ayuUEkcUbI8wHn/7bz4aHrjtdDyl3F/6GX3cyHbs/BQh1kWCr+t8EJUWVPl5s4jmL3tst/Q==", "dev": true, "requires": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.3.0-beta.1", + "@yext/search-core": "^2.3.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "@yext/search-headless-react": { - "version": "2.2.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0-beta.0.tgz", - "integrity": "sha512-fyMHbIWy+xJxZ36YSmBCCFX07fVUboxuVXNIBFn9u7L4DI97daulZE4v0SCtyHm3opgwLvCcx/OggmhLqfbhMQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0.tgz", + "integrity": "sha512-x2Sx7uS3w5E8RfuIpPsQZGWTGNKS9kRWDlQReUngzxP+UhoBQ8L7qNTGGbJLZ+LUpjRqTS1RuZP4scljbla//g==", "dev": true, "requires": { - "@yext/search-headless": "^2.2.1-beta.0", + "@yext/search-headless": "^2.3.0", "use-sync-external-store": "^1.1.0" } }, diff --git a/package.json b/package.json index 582462000..2ed070921 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.2.0-beta.0", + "version": "1.2.0", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", @@ -76,7 +76,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -93,7 +93,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" }, diff --git a/tests/components/Facets.stories.tsx b/tests/components/Facets.stories.tsx index 5e92e10f3..e24affa12 100644 --- a/tests/components/Facets.stories.tsx +++ b/tests/components/Facets.stories.tsx @@ -4,6 +4,7 @@ import { generateMockedHeadless } from '../__fixtures__/search-headless'; import { RecursivePartial } from '../__utils__/mocks'; import { DisplayableFacets } from '../__fixtures__/data/filters'; import { Facets, FacetsProps } from '../../src'; +import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; const meta: ComponentMeta = { title: 'Facets', @@ -13,7 +14,15 @@ export default meta; const mockedHeadlessState: RecursivePartial = { filters: { - facets: DisplayableFacets + facets: [ + ...DisplayableFacets, + createHierarchicalFacet([ + 'food', + 'food > fruit', + { value: 'food > fruit > banana', selected: true }, + 'food > fruit > apple', + ]) + ] } }; From 9597e9aae57f12b0369fa9291046a42ca4f525d8 Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Fri, 12 May 2023 14:27:30 -0400 Subject: [PATCH 13/23] Add Singular StandardFacet (#370) Add Singular StandardFacet This change adds a singular StandardFacet that can be used to override facets rendered in Facets. --- docs/search-ui-react.facetprops.md | 15 +++ docs/search-ui-react.facets.md | 2 + ...earch-ui-react.facetscssclasses.divider.md | 11 ++ docs/search-ui-react.facetscssclasses.md | 1 + docs/search-ui-react.facetsprops.children.md | 18 +++ ...h-ui-react.facetsprops.excludedfieldids.md | 13 ++ docs/search-ui-react.facetsprops.md | 3 + ...rch-ui-react.facetsprops.searchonchange.md | 13 ++ docs/search-ui-react.md | 3 + docs/search-ui-react.standardfacet.md | 26 ++++ ...ui-react.standardfacetprops.collapsible.md | 13 ++ ...act.standardfacetprops.customcssclasses.md | 13 ++ ...eact.standardfacetprops.defaultexpanded.md | 13 ++ ...rch-ui-react.standardfacetprops.fieldid.md | 13 ++ ...earch-ui-react.standardfacetprops.label.md | 13 ++ docs/search-ui-react.standardfacetprops.md | 27 ++++ ...-react.standardfacetprops.showmorelimit.md | 13 ++ ...act.standardfacetprops.showoptioncounts.md | 13 ++ ...act.standardfacetprops.transformoptions.md | 13 ++ docs/search-ui-react.standardfacets.md | 5 + ...earch-ui-react.standardfacetscssclasses.md | 5 + docs/search-ui-react.standardfacetsprops.md | 5 + etc/search-ui-react.api.md | 29 ++++- src/components/FacetProps.tsx | 64 +++++++++ src/components/Facets.tsx | 121 ++++++++++++++---- src/components/Filters/FacetsProvider.tsx | 4 +- src/components/StandardFacetContent.tsx | 39 ++++++ src/components/StandardFacets.tsx | 9 +- src/components/index.ts | 10 +- src/utils/filterutils.tsx | 7 + test-site/package-lock.json | 8 +- tests/components/Facets.test.tsx | 30 ++++- tests/components/StandardFacet.stories.tsx | 16 +++ 33 files changed, 549 insertions(+), 39 deletions(-) create mode 100644 docs/search-ui-react.facetprops.md create mode 100644 docs/search-ui-react.facetscssclasses.divider.md create mode 100644 docs/search-ui-react.facetsprops.children.md create mode 100644 docs/search-ui-react.facetsprops.excludedfieldids.md create mode 100644 docs/search-ui-react.facetsprops.searchonchange.md create mode 100644 docs/search-ui-react.standardfacet.md create mode 100644 docs/search-ui-react.standardfacetprops.collapsible.md create mode 100644 docs/search-ui-react.standardfacetprops.customcssclasses.md create mode 100644 docs/search-ui-react.standardfacetprops.defaultexpanded.md create mode 100644 docs/search-ui-react.standardfacetprops.fieldid.md create mode 100644 docs/search-ui-react.standardfacetprops.label.md create mode 100644 docs/search-ui-react.standardfacetprops.md create mode 100644 docs/search-ui-react.standardfacetprops.showmorelimit.md create mode 100644 docs/search-ui-react.standardfacetprops.showoptioncounts.md create mode 100644 docs/search-ui-react.standardfacetprops.transformoptions.md create mode 100644 src/components/FacetProps.tsx create mode 100644 src/components/StandardFacetContent.tsx create mode 100644 tests/components/StandardFacet.stories.tsx diff --git a/docs/search-ui-react.facetprops.md b/docs/search-ui-react.facetprops.md new file mode 100644 index 000000000..cea2942ba --- /dev/null +++ b/docs/search-ui-react.facetprops.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetProps](./search-ui-react.facetprops.md) + +## FacetProps type + +Props for a single facet component. + +Signature: + +```typescript +export declare type FacetProps = StandardFacetProps; +``` +References: [StandardFacetProps](./search-ui-react.standardfacetprops.md) + diff --git a/docs/search-ui-react.facets.md b/docs/search-ui-react.facets.md index 8c8b5b2a6..299d0c122 100644 --- a/docs/search-ui-react.facets.md +++ b/docs/search-ui-react.facets.md @@ -28,3 +28,5 @@ A React component for facets This component is a quick way of getting facets on the page, and it will render standard facets, numerical facets, and hierarchical facets. The [StandardFacets()](./search-ui-react.standardfacets.md), [NumericalFacets()](./search-ui-react.numericalfacets.md), and [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) components can be used instead for more control over facet configuration. +To override a single facet, use [StandardFacet()](./search-ui-react.standardfacet.md). + diff --git a/docs/search-ui-react.facetscssclasses.divider.md b/docs/search-ui-react.facetscssclasses.divider.md new file mode 100644 index 000000000..7a478a5f7 --- /dev/null +++ b/docs/search-ui-react.facetscssclasses.divider.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsCssClasses](./search-ui-react.facetscssclasses.md) > [divider](./search-ui-react.facetscssclasses.divider.md) + +## FacetsCssClasses.divider property + +Signature: + +```typescript +divider?: string; +``` diff --git a/docs/search-ui-react.facetscssclasses.md b/docs/search-ui-react.facetscssclasses.md index 04c2b7fbf..2aebb472b 100644 --- a/docs/search-ui-react.facetscssclasses.md +++ b/docs/search-ui-react.facetscssclasses.md @@ -16,5 +16,6 @@ export interface FacetsCssClasses | Property | Type | Description | | --- | --- | --- | +| [divider?](./search-ui-react.facetscssclasses.divider.md) | string | (Optional) | | [facetsContainer?](./search-ui-react.facetscssclasses.facetscontainer.md) | string | (Optional) | diff --git a/docs/search-ui-react.facetsprops.children.md b/docs/search-ui-react.facetsprops.children.md new file mode 100644 index 000000000..77f1227ec --- /dev/null +++ b/docs/search-ui-react.facetsprops.children.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [children](./search-ui-react.facetsprops.children.md) + +## FacetsProps.children property + +The custom facet components that will override the default rendering. + +Signature: + +```typescript +children?: ReactElement[] | ReactElement | undefined | null; +``` + +## Remarks + +Supported components include [StandardFacet()](./search-ui-react.standardfacet.md). + diff --git a/docs/search-ui-react.facetsprops.excludedfieldids.md b/docs/search-ui-react.facetsprops.excludedfieldids.md new file mode 100644 index 000000000..ea330c1e5 --- /dev/null +++ b/docs/search-ui-react.facetsprops.excludedfieldids.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [excludedFieldIds](./search-ui-react.facetsprops.excludedfieldids.md) + +## FacetsProps.excludedFieldIds property + +List of filter ids that should not be displayed. + +Signature: + +```typescript +excludedFieldIds?: string[]; +``` diff --git a/docs/search-ui-react.facetsprops.md b/docs/search-ui-react.facetsprops.md index 7d5cc5f5c..6a2ac75a1 100644 --- a/docs/search-ui-react.facetsprops.md +++ b/docs/search-ui-react.facetsprops.md @@ -16,5 +16,8 @@ export interface FacetsProps | Property | Type | Description | | --- | --- | --- | +| [children?](./search-ui-react.facetsprops.children.md) | ReactElement\[\] \| ReactElement \| undefined \| null | (Optional) The custom facet components that will override the default rendering. | | [customCssClasses?](./search-ui-react.facetsprops.customcssclasses.md) | [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | (Optional) CSS classes for customizing the component styling. | +| [excludedFieldIds?](./search-ui-react.facetsprops.excludedfieldids.md) | string\[\] | (Optional) List of filter ids that should not be displayed. | +| [searchOnChange?](./search-ui-react.facetsprops.searchonchange.md) | boolean | (Optional) Whether or not a search is automatically run when a filter is selected. Defaults to true. | diff --git a/docs/search-ui-react.facetsprops.searchonchange.md b/docs/search-ui-react.facetsprops.searchonchange.md new file mode 100644 index 000000000..90c0cb05e --- /dev/null +++ b/docs/search-ui-react.facetsprops.searchonchange.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [searchOnChange](./search-ui-react.facetsprops.searchonchange.md) + +## FacetsProps.searchOnChange property + +Whether or not a search is automatically run when a filter is selected. Defaults to true. + +Signature: + +```typescript +searchOnChange?: boolean; +``` diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 7ddfd54cf..26bc33e2f 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -33,6 +33,7 @@ | [SearchBar({ placeholder, geolocationOptions, hideRecentSearches, visualAutocompleteConfig, showVerticalLinks, onSelectVerticalLink, verticalKeyToLabel, recentSearchesLimit, customCssClasses, onSearch })](./search-ui-react.searchbar.md) | Renders a SearchBar that is hooked up with an InputDropdown component. | | [SpellCheck({ customCssClasses, onClick })](./search-ui-react.spellcheck.md) | Renders a suggested query if the Search API provides one. | | [StandardCard(props)](./search-ui-react.standardcard.md) | This Component renders the base result card. | +| [StandardFacet(props)](./search-ui-react.standardfacet.md) | A component that displays a single standard facet. Use this to override the default rendering. | | [StandardFacets(props)](./search-ui-react.standardfacets.md) | A component that displays simple facets applicable to the current vertical search. | | [StandardSection(props)](./search-ui-react.standardsection.md) | A component that displays all the results for a vertical using a standard section template. | | [StaticFilters(props)](./search-ui-react.staticfilters.md) | A component that displays a group of user-configured field value filters that will be applied to the current vertical search. | @@ -94,6 +95,7 @@ | [StandardCardCssClasses](./search-ui-react.standardcardcssclasses.md) | The CSS class interface used for the StandardCardDisplay. | | [StandardCardData](./search-ui-react.standardcarddata.md) | The data used by the [StandardCard()](./search-ui-react.standardcard.md) and taken from the original Result. | | [StandardCardProps](./search-ui-react.standardcardprops.md) | Props for a StandardCard. | +| [StandardFacetProps](./search-ui-react.standardfacetprops.md) | Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. | | [StandardFacetsCssClasses](./search-ui-react.standardfacetscssclasses.md) | The CSS class interface for [StandardFacets()](./search-ui-react.standardfacets.md). | | [StandardFacetsProps](./search-ui-react.standardfacetsprops.md) | Props for the [StandardFacets()](./search-ui-react.standardfacets.md) component. | | [StandardSectionCssClasses](./search-ui-react.standardsectioncssclasses.md) | The CSS class interface used for [StandardSection()](./search-ui-react.standardsection.md). | @@ -129,6 +131,7 @@ | [CoordinateGetter](./search-ui-react.coordinategetter.md) | A function use to derive a result's coordinate. | | [DefaultRawDataType](./search-ui-react.defaultrawdatatype.md) | The default type for "rawData" field of type Result. | | [DropdownItemProps](./search-ui-react.dropdownitemprops.md) | Props for the [DropdownItem()](./search-ui-react.dropdownitem.md). | +| [FacetProps](./search-ui-react.facetprops.md) | Props for a single facet component. | | [FeedbackType](./search-ui-react.feedbacktype.md) | Analytics event types for quality feedback. | | [FocusedItemData](./search-ui-react.focuseditemdata.md) | The data associated with the currently focused item. | | [OnDragHandler](./search-ui-react.ondraghandler.md) | A function which is called when user drag the map. | diff --git a/docs/search-ui-react.standardfacet.md b/docs/search-ui-react.standardfacet.md new file mode 100644 index 000000000..6592863d3 --- /dev/null +++ b/docs/search-ui-react.standardfacet.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacet](./search-ui-react.standardfacet.md) + +## StandardFacet() function + +A component that displays a single standard facet. Use this to override the default rendering. + +Signature: + +```typescript +export declare function StandardFacet(props: StandardFacetProps): null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | [StandardFacetProps](./search-ui-react.standardfacetprops.md) | [StandardFacetProps](./search-ui-react.standardfacetprops.md) | + +Returns: + +null + +ReactElement + diff --git a/docs/search-ui-react.standardfacetprops.collapsible.md b/docs/search-ui-react.standardfacetprops.collapsible.md new file mode 100644 index 000000000..82508d336 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.collapsible.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [collapsible](./search-ui-react.standardfacetprops.collapsible.md) + +## StandardFacetProps.collapsible property + +Whether or not the filter is collapsible. Defaults to true. + +Signature: + +```typescript +collapsible?: boolean; +``` diff --git a/docs/search-ui-react.standardfacetprops.customcssclasses.md b/docs/search-ui-react.standardfacetprops.customcssclasses.md new file mode 100644 index 000000000..0c8758dcd --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.customcssclasses.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [customCssClasses](./search-ui-react.standardfacetprops.customcssclasses.md) + +## StandardFacetProps.customCssClasses property + +CSS classes for customizing the component styling. + +Signature: + +```typescript +customCssClasses?: FilterGroupCssClasses; +``` diff --git a/docs/search-ui-react.standardfacetprops.defaultexpanded.md b/docs/search-ui-react.standardfacetprops.defaultexpanded.md new file mode 100644 index 000000000..c8d2e25d6 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.defaultexpanded.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [defaultExpanded](./search-ui-react.standardfacetprops.defaultexpanded.md) + +## StandardFacetProps.defaultExpanded property + +If the filter group is collapsible, whether or not it should start out expanded. Defaults to true. + +Signature: + +```typescript +defaultExpanded?: boolean; +``` diff --git a/docs/search-ui-react.standardfacetprops.fieldid.md b/docs/search-ui-react.standardfacetprops.fieldid.md new file mode 100644 index 000000000..73096426d --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.fieldid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [fieldId](./search-ui-react.standardfacetprops.fieldid.md) + +## StandardFacetProps.fieldId property + +The fieldId corresponding to the facet + +Signature: + +```typescript +fieldId: string; +``` diff --git a/docs/search-ui-react.standardfacetprops.label.md b/docs/search-ui-react.standardfacetprops.label.md new file mode 100644 index 000000000..a1e0c7a0f --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.label.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [label](./search-ui-react.standardfacetprops.label.md) + +## StandardFacetProps.label property + +The label of the facet. Defaults to facet's displayName if not provided. + +Signature: + +```typescript +label?: string; +``` diff --git a/docs/search-ui-react.standardfacetprops.md b/docs/search-ui-react.standardfacetprops.md new file mode 100644 index 000000000..62e93d797 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) + +## StandardFacetProps interface + +Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. + +Signature: + +```typescript +export interface StandardFacetProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [collapsible?](./search-ui-react.standardfacetprops.collapsible.md) | boolean | (Optional) Whether or not the filter is collapsible. Defaults to true. | +| [customCssClasses?](./search-ui-react.standardfacetprops.customcssclasses.md) | [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) | (Optional) CSS classes for customizing the component styling. | +| [defaultExpanded?](./search-ui-react.standardfacetprops.defaultexpanded.md) | boolean | (Optional) If the filter group is collapsible, whether or not it should start out expanded. Defaults to true. | +| [fieldId](./search-ui-react.standardfacetprops.fieldid.md) | string | The fieldId corresponding to the facet | +| [label?](./search-ui-react.standardfacetprops.label.md) | string | (Optional) The label of the facet. Defaults to facet's displayName if not provided. | +| [showMoreLimit?](./search-ui-react.standardfacetprops.showmorelimit.md) | number | (Optional) Limit on the number of options to be displayed. Defaults to 10. | +| [showOptionCounts?](./search-ui-react.standardfacetprops.showoptioncounts.md) | boolean | (Optional) Whether or not to show the option counts for each filter. Defaults to true. | +| [transformOptions?](./search-ui-react.standardfacetprops.transformoptions.md) | (options: DisplayableFacetOption\[\]) => DisplayableFacetOption\[\] | (Optional) A function to transform facet's options. | + diff --git a/docs/search-ui-react.standardfacetprops.showmorelimit.md b/docs/search-ui-react.standardfacetprops.showmorelimit.md new file mode 100644 index 000000000..40dae1672 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.showmorelimit.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [showMoreLimit](./search-ui-react.standardfacetprops.showmorelimit.md) + +## StandardFacetProps.showMoreLimit property + +Limit on the number of options to be displayed. Defaults to 10. + +Signature: + +```typescript +showMoreLimit?: number; +``` diff --git a/docs/search-ui-react.standardfacetprops.showoptioncounts.md b/docs/search-ui-react.standardfacetprops.showoptioncounts.md new file mode 100644 index 000000000..a449f9916 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.showoptioncounts.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [showOptionCounts](./search-ui-react.standardfacetprops.showoptioncounts.md) + +## StandardFacetProps.showOptionCounts property + +Whether or not to show the option counts for each filter. Defaults to true. + +Signature: + +```typescript +showOptionCounts?: boolean; +``` diff --git a/docs/search-ui-react.standardfacetprops.transformoptions.md b/docs/search-ui-react.standardfacetprops.transformoptions.md new file mode 100644 index 000000000..fb174ecb3 --- /dev/null +++ b/docs/search-ui-react.standardfacetprops.transformoptions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [StandardFacetProps](./search-ui-react.standardfacetprops.md) > [transformOptions](./search-ui-react.standardfacetprops.transformoptions.md) + +## StandardFacetProps.transformOptions property + +A function to transform facet's options. + +Signature: + +```typescript +transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[]; +``` diff --git a/docs/search-ui-react.standardfacets.md b/docs/search-ui-react.standardfacets.md index f786896b3..ae2d5ce08 100644 --- a/docs/search-ui-react.standardfacets.md +++ b/docs/search-ui-react.standardfacets.md @@ -4,6 +4,11 @@ ## StandardFacets() function +> Warning: This API is now obsolete. +> +> Use [Facets()](./search-ui-react.facets.md) instead. +> + A component that displays simple facets applicable to the current vertical search. Signature: diff --git a/docs/search-ui-react.standardfacetscssclasses.md b/docs/search-ui-react.standardfacetscssclasses.md index f7cec29b4..06f87d59f 100644 --- a/docs/search-ui-react.standardfacetscssclasses.md +++ b/docs/search-ui-react.standardfacetscssclasses.md @@ -4,6 +4,11 @@ ## StandardFacetsCssClasses interface +> Warning: This API is now obsolete. +> +> Use [Facets()](./search-ui-react.facets.md) instead. +> + The CSS class interface for [StandardFacets()](./search-ui-react.standardfacets.md). Signature: diff --git a/docs/search-ui-react.standardfacetsprops.md b/docs/search-ui-react.standardfacetsprops.md index 81260b627..903e12f43 100644 --- a/docs/search-ui-react.standardfacetsprops.md +++ b/docs/search-ui-react.standardfacetsprops.md @@ -4,6 +4,11 @@ ## StandardFacetsProps interface +> Warning: This API is now obsolete. +> +> Use [Facets()](./search-ui-react.facets.md) instead. +> + Props for the [StandardFacets()](./search-ui-react.standardfacets.md) component. Signature: diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index b5d09e778..82b5d8ffb 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -10,6 +10,7 @@ import { AnalyticsConfig } from '@yext/analytics'; import { AnalyticsService } from '@yext/analytics'; import { AutocompleteResponse } from '@yext/search-headless-react'; import { DirectAnswer as DirectAnswer_2 } from '@yext/search-headless-react'; +import { DisplayableFacetOption } from '@yext/search-headless-react'; import { FieldValueStaticFilter } from '@yext/search-headless-react'; import { FilterSearchResponse } from '@yext/search-headless-react'; import { HighlightedValue } from '@yext/search-headless-react'; @@ -21,6 +22,7 @@ import { Matcher } from '@yext/search-headless-react'; import { NumberRangeValue } from '@yext/search-headless-react'; import { PropsWithChildren } from 'react'; import { QuerySource } from '@yext/search-headless-react'; +import { ReactElement } from 'react'; import { Result } from '@yext/search-headless-react'; import { SearchActions } from '@yext/search-headless-react'; import { SearchHeadless } from '@yext/search-headless-react'; @@ -200,18 +202,26 @@ export function executeAutocomplete(searchActions: SearchActions): Promise; +// @public +export type FacetProps = StandardFacetProps; + // @public export function Facets(props: FacetsProps): JSX.Element; // @public export interface FacetsCssClasses { + // (undocumented) + divider?: string; // (undocumented) facetsContainer?: string; } // @public export interface FacetsProps { + children?: ReactElement[] | ReactElement | undefined | null; customCssClasses?: FacetsCssClasses; + excludedFieldIds?: string[]; + searchOnChange?: boolean; } // @public @@ -682,9 +692,24 @@ export interface StandardCardProps extends CardProps } // @public -export function StandardFacets(props: StandardFacetsProps): JSX.Element; +export function StandardFacet(props: StandardFacetProps): null; // @public +export interface StandardFacetProps { + collapsible?: boolean; + customCssClasses?: FilterGroupCssClasses; + defaultExpanded?: boolean; + fieldId: string; + label?: string; + showMoreLimit?: number; + showOptionCounts?: boolean; + transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[]; +} + +// @public @deprecated +export function StandardFacets(props: StandardFacetsProps): JSX.Element; + +// @public @deprecated export interface StandardFacetsCssClasses extends FilterGroupCssClasses { // (undocumented) divider?: string; @@ -692,7 +717,7 @@ export interface StandardFacetsCssClasses extends FilterGroupCssClasses { standardFacetsContainer?: string; } -// @public +// @public @deprecated export interface StandardFacetsProps { collapsible?: boolean; customCssClasses?: StandardFacetsCssClasses; diff --git a/src/components/FacetProps.tsx b/src/components/FacetProps.tsx new file mode 100644 index 000000000..de5de039a --- /dev/null +++ b/src/components/FacetProps.tsx @@ -0,0 +1,64 @@ +import { DisplayableFacetOption } from '@yext/search-headless-react'; +import { FilterGroupCssClasses } from './FilterGroup'; +import { ReactElement } from 'react'; + +/** + * The CSS class interface for {@link Facets}. + * + * @public + */ +export interface FacetsCssClasses { + facetsContainer?: string, + divider?: string +} + +/** + * Props for the {@link Facets} component. + * + * @public + */ +export interface FacetsProps { + /** Whether or not a search is automatically run when a filter is selected. Defaults to true. */ + searchOnChange?: boolean, + /** CSS classes for customizing the component styling. */ + customCssClasses?: FacetsCssClasses, + /** List of filter ids that should not be displayed. */ + excludedFieldIds?: string[], + /** The custom facet components that will override the default rendering. + * + * @remarks + * Supported components include {@link StandardFacet}. + */ + children?: ReactElement[] | ReactElement | undefined | null +} + +/** + * Props for the {@link StandardFacet} component. + * + * @public + */ +export interface StandardFacetProps { + /** The fieldId corresponding to the facet */ + fieldId: string, + /** The label of the facet. Defaults to facet's displayName if not provided. */ + label?: string, + /** A function to transform facet's options. */ + transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[], + /** {@inheritDoc FilterGroupProps.collapsible} */ + collapsible?: boolean, + /** {@inheritDoc FilterGroupProps.defaultExpanded} */ + defaultExpanded?: boolean, + /** Whether or not to show the option counts for each filter. Defaults to true. */ + showOptionCounts?: boolean, + /** Limit on the number of options to be displayed. Defaults to 10. */ + showMoreLimit?: number, + /** CSS classes for customizing the component styling. */ + customCssClasses?: FilterGroupCssClasses +} + +/** + * Props for a single facet component. + * + * @public + */ +export type FacetProps = StandardFacetProps; diff --git a/src/components/Facets.tsx b/src/components/Facets.tsx index 1c5b90c2a..d03ebf5bb 100644 --- a/src/components/Facets.tsx +++ b/src/components/Facets.tsx @@ -1,23 +1,20 @@ -import { StandardFacets } from './StandardFacets'; +import { FacetsProvider } from './Filters'; +import { StandardFacetContent } from './StandardFacetContent'; +import { + FacetProps, + FacetsProps, + StandardFacetProps +} from './FacetProps'; +import { isNumericalFacet, isStringFacet } from '../utils/filterutils'; +import { FilterDivider } from './FilterDivider'; +import { Fragment, ReactElement } from 'react'; import { NumericalFacets } from './NumericalFacets'; -/** - * The CSS class interface for {@link Facets}. - * - * @public - */ -export interface FacetsCssClasses { - facetsContainer?: string -} - -/** - * Props for the {@link Facets} component. - * - * @public - */ -export interface FacetsProps { - /** CSS classes for customizing the component styling. */ - customCssClasses?: FacetsCssClasses +/** @internal */ +enum FacetType { + STANDARD = 'STANDARD', + NUMERICAL = 'NUMERICAL', + HIERARCHICAL = 'HIERARCHICAL' } /** @@ -29,17 +26,95 @@ export interface FacetsProps { * and {@link HierarchicalFacets} components can be used instead for more control over facet * configuration. * + * To override a single facet, use {@link StandardFacet}. + * * @param props - {@link FacetsProps} * @returns A React component for facets * * @public */ export function Facets(props: FacetsProps) { - const { customCssClasses = {} } = props; + const { searchOnChange, children, customCssClasses = {} } = props; + + const fieldIdToCustomFacetProps = new Map(); + if (children) { + (Array.isArray(children) ? children : [children]) + .forEach(child => fieldIdToCustomFacetProps.set(child.props.fieldId, child)); + } + return ( -
- - +
+ + {facets => facets + .map((facet, i) => { + let facetType: FacetType = FacetType.STANDARD; + let facetProps: FacetProps = { + fieldId: facet.fieldId, + label: facet.displayName, + }; + if (fieldIdToCustomFacetProps.has(facet.fieldId)) { + const customFacetElement: ReactElement = fieldIdToCustomFacetProps.get(facet.fieldId); + facetProps = customFacetElement.props; + facetType = getFacetTypeFromReactElementType( + (typeof customFacetElement.type === 'function') ? customFacetElement.type.name : ''); + } else { + if (isStringFacet(facet)) { + facetType = FacetType.STANDARD; + } else if (isNumericalFacet(facet)) { + facetType = FacetType.NUMERICAL; + } + } + + let facetComponent: ReactElement; + switch (facetType) { + case FacetType.NUMERICAL: + facetComponent =
; + break; + case FacetType.STANDARD: + // fall through + default: + facetComponent = ( + ); + } + + return ( + + {facetComponent} + {(i < facets.length - 1) && } + + ); + }) + } + +
); -} \ No newline at end of file +} + +/** + * A component that displays a single standard facet. Use this to override the default rendering. + * + * @param props - {@link StandardFacetProps} + * @returns ReactElement + * @public + */ +export function StandardFacet(props: StandardFacetProps) { return null; } + +/** + * Returns the type of the facet based on the props. + * @param elementType - string + * @returns {@link FacetType} + * + * @internal + */ +export function getFacetTypeFromReactElementType(elementType: string) { + switch (elementType) { + case StandardFacet.toString(): + // fall through + default: + return FacetType.STANDARD; + } +} diff --git a/src/components/Filters/FacetsProvider.tsx b/src/components/Filters/FacetsProvider.tsx index 52f4f5c3b..3aac3bc4b 100644 --- a/src/components/Filters/FacetsProvider.tsx +++ b/src/components/Filters/FacetsProvider.tsx @@ -31,8 +31,8 @@ export interface FacetsProviderProps { } /** - * The Facets component is a wrapper component around {@link Filters} that updates facet options - * when a child filter is updated. + * The FacetsProvider component is a wrapper component around {@link Filters} that updates facet + * options when a child filter is updated. * * The representation of the facets is configured using a FACC (function as a child component) * The FACC is passed the facets data, and is intended for use with components like diff --git a/src/components/StandardFacetContent.tsx b/src/components/StandardFacetContent.tsx new file mode 100644 index 000000000..a1155a7c7 --- /dev/null +++ b/src/components/StandardFacetContent.tsx @@ -0,0 +1,39 @@ +import { FilterGroup } from './FilterGroup'; +import { DisplayableFacet } from '@yext/search-headless-react'; +import { StandardFacetProps } from './FacetProps'; + +/** + * A component that displays the content of a standard facet. + * + * @param props - props to render the component + * @returns A React component for the content of a standard facet + * + * @internal + */ +export function StandardFacetContent({ + fieldId, + customCssClasses, + transformOptions, + facet, + label = '', + showMoreLimit = 10, + showOptionCounts = true, + ...filterGroupProps +}: StandardFacetProps & { facet: DisplayableFacet }) { + const options = facet.options || []; + const transformedOptions = transformOptions ? (transformOptions(options) || []) : options; + + return ( + { + return showOptionCounts ? { ...o, resultsCount: o.count } : o; + })} + title={label} + customCssClasses={customCssClasses} + showMoreLimit={showMoreLimit} + searchable={facet?.options.length > showMoreLimit} + {...filterGroupProps} + /> + ); +} diff --git a/src/components/StandardFacets.tsx b/src/components/StandardFacets.tsx index afa4607ca..3b2ade707 100644 --- a/src/components/StandardFacets.tsx +++ b/src/components/StandardFacets.tsx @@ -1,12 +1,13 @@ import { FacetsProvider } from './Filters'; import { FilterGroup, FilterGroupCssClasses } from './FilterGroup'; import { Fragment } from 'react'; -import { DisplayableFacet } from '@yext/search-headless-react'; import { FilterDivider } from './FilterDivider'; +import { isStringFacet } from '../utils/filterutils'; /** * The CSS class interface for {@link StandardFacets}. * + * @deprecated Use {@link Facets} instead. * @public */ export interface StandardFacetsCssClasses extends FilterGroupCssClasses { @@ -17,6 +18,7 @@ export interface StandardFacetsCssClasses extends FilterGroupCssClasses { /** * Props for the {@link StandardFacets} component. * + * @deprecated Use {@link Facets} instead. * @public */ export interface StandardFacetsProps { @@ -56,6 +58,7 @@ export interface StandardFacetsProps { * @param props - {@link StandardFacetsProps} * @returns A React component for facets * + * @deprecated Use {@link Facets} instead. * @public */ export function StandardFacets(props: StandardFacetsProps) { @@ -93,7 +96,3 @@ export function StandardFacets(props: StandardFacetsProps) {
); } - -function isStringFacet(facet: DisplayableFacet): boolean { - return facet.options.length > 0 && typeof facet.options[0].value === 'string'; -} \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts index f12aa093e..ba055d85a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -127,10 +127,16 @@ export { export { Facets, - FacetsCssClasses, - FacetsProps + StandardFacet } from './Facets'; +export { + FacetsCssClasses, + FacetsProps, + FacetProps, + StandardFacetProps +} from './FacetProps'; + export { FilterGroupProps, FilterGroupCssClasses diff --git a/src/utils/filterutils.tsx b/src/utils/filterutils.tsx index 7347bf8f5..0f746e42c 100644 --- a/src/utils/filterutils.tsx +++ b/src/utils/filterutils.tsx @@ -17,6 +17,13 @@ export function isNumberRangeValue(obj: unknown): obj is NumberRangeValue { return typeof obj === 'object' && !!obj && ('start' in obj || 'end' in obj); } +/** + * Checks if the facet is a string facet with string options. + */ +export function isStringFacet(facet: DisplayableFacet): boolean { + return facet.options.length > 0 && typeof facet.options[0].value === 'string'; +} + /** * Checks if the facet is a numerical facet with number range filter options. */ diff --git a/test-site/package-lock.json b/test-site/package-lock.json index 762030b58..5a6e34d16 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,7 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", - "version": "1.1.0", + "version": "1.2.0-beta.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -80,7 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -97,7 +97,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -20112,7 +20112,7 @@ "@typescript-eslint/parser": "^5.16.0", "@yext/analytics": "^0.2.0-beta.3", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.1.0", + "@yext/search-headless-react": "^2.2.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "classnames": "^2.3.1", diff --git a/tests/components/Facets.test.tsx b/tests/components/Facets.test.tsx index 46ca478c8..e1b70fde3 100644 --- a/tests/components/Facets.test.tsx +++ b/tests/components/Facets.test.tsx @@ -5,8 +5,9 @@ import { } from '@yext/search-headless-react'; import { mockAnswersHooks, mockAnswersState } from '../__utils__/mocks'; import { DisplayableFacets } from '../__fixtures__/data/filters'; -import { Facets } from '../../src'; +import { Facets, StandardFacet, StandardFacetProps } from '../../src'; import { getOptionLabelText } from '../__utils__/facets'; +import { DisplayableFacetOption } from '@yext/search-core'; const mockedState: Partial = { filters: { @@ -88,4 +89,31 @@ describe('Facets', () => { expect(screen.queryByText(`${o.displayName} (${o.count})}`)).toBeNull(); }); }); + + it('Properly renders an override standard facet if present', () => { + const mockTransformOptions = (options: DisplayableFacetOption[]) => + options.map(option => ({ ...option, displayName: `my ${option.displayName}` })); + + const overrideFieldId = 'products'; + const overrideLabel = 'My Products'; + const props: StandardFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + transformOptions: mockTransformOptions, + }; + + render( + + + ); + const regularFilter = DisplayableFacets[0]; + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.queryByText(regularFilter.displayName)).toBeNull(); + expect( + screen + .getByText( + `my ${regularFilter.options[0].displayName} (${regularFilter.options[0].count})`)) + .toBeDefined(); + }); }); \ No newline at end of file diff --git a/tests/components/StandardFacet.stories.tsx b/tests/components/StandardFacet.stories.tsx new file mode 100644 index 000000000..cbb3255c8 --- /dev/null +++ b/tests/components/StandardFacet.stories.tsx @@ -0,0 +1,16 @@ +import { ComponentMeta, Story } from '@storybook/react'; +import { Facets, StandardFacetProps, StandardFacet } from '../../src'; + +const meta: ComponentMeta = { + title: 'StandardFacet', + component: StandardFacet +}; +export default meta; + +export const Primary: Story = (args) => { + return ( + + + + ); +}; \ No newline at end of file From f936f9b7873fd859e737ad8cd04f2e0c98940ff9 Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Tue, 16 May 2023 14:20:57 -0400 Subject: [PATCH 14/23] Add Numerical Facet (#372) Add Numerical Facet This change adds a singular NumericalFacet that can be used to override facets rendered in Facets. J=BACK-2221 TEST=auto,manual Ran npm run test and confirmed all tests pass. Manually added a NumericalFacet with Facets on the people's page under test-site/. Confirmed the facet gets overridden correctly. --- docs/search-ui-react.facetprops.md | 4 +- docs/search-ui-react.facets.md | 2 +- docs/search-ui-react.facetsprops.children.md | 2 +- docs/search-ui-react.md | 2 + docs/search-ui-react.numericalfacet.md | 26 ++++++++++ ...ct.numericalfacetprops.customcssclasses.md | 13 +++++ ...umericalfacetprops.getfilterdisplayname.md | 18 +++++++ ...i-react.numericalfacetprops.inputprefix.md | 13 +++++ docs/search-ui-react.numericalfacetprops.md | 24 +++++++++ ...ct.numericalfacetprops.showoptioncounts.md | 13 +++++ etc/search-ui-react.api.md | 13 ++++- src/components/FacetProps.tsx | 31 +++++++++++- src/components/Facets.tsx | 28 +++++++---- src/components/NumericalFacetContent.tsx | 50 +++++++++++++++++++ src/components/index.ts | 6 ++- tests/components/Facets.test.tsx | 25 +++++++++- tests/components/NumericalFacet.stories.tsx | 37 ++++++++++++++ tests/components/StandardFacet.stories.tsx | 37 +++++++++++--- 18 files changed, 315 insertions(+), 29 deletions(-) create mode 100644 docs/search-ui-react.numericalfacet.md create mode 100644 docs/search-ui-react.numericalfacetprops.customcssclasses.md create mode 100644 docs/search-ui-react.numericalfacetprops.getfilterdisplayname.md create mode 100644 docs/search-ui-react.numericalfacetprops.inputprefix.md create mode 100644 docs/search-ui-react.numericalfacetprops.md create mode 100644 docs/search-ui-react.numericalfacetprops.showoptioncounts.md create mode 100644 src/components/NumericalFacetContent.tsx create mode 100644 tests/components/NumericalFacet.stories.tsx diff --git a/docs/search-ui-react.facetprops.md b/docs/search-ui-react.facetprops.md index cea2942ba..c5429d55f 100644 --- a/docs/search-ui-react.facetprops.md +++ b/docs/search-ui-react.facetprops.md @@ -9,7 +9,7 @@ Props for a single facet component. Signature: ```typescript -export declare type FacetProps = StandardFacetProps; +export declare type FacetProps = StandardFacetProps | NumericalFacetProps; ``` -References: [StandardFacetProps](./search-ui-react.standardfacetprops.md) +References: [StandardFacetProps](./search-ui-react.standardfacetprops.md), [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) diff --git a/docs/search-ui-react.facets.md b/docs/search-ui-react.facets.md index 299d0c122..e24bc7ad3 100644 --- a/docs/search-ui-react.facets.md +++ b/docs/search-ui-react.facets.md @@ -28,5 +28,5 @@ A React component for facets This component is a quick way of getting facets on the page, and it will render standard facets, numerical facets, and hierarchical facets. The [StandardFacets()](./search-ui-react.standardfacets.md), [NumericalFacets()](./search-ui-react.numericalfacets.md), and [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) components can be used instead for more control over facet configuration. -To override a single facet, use [StandardFacet()](./search-ui-react.standardfacet.md). +To override a single facet, use [StandardFacet()](./search-ui-react.standardfacet.md) or [NumericalFacet()](./search-ui-react.numericalfacet.md). diff --git a/docs/search-ui-react.facetsprops.children.md b/docs/search-ui-react.facetsprops.children.md index 77f1227ec..1f661f275 100644 --- a/docs/search-ui-react.facetsprops.children.md +++ b/docs/search-ui-react.facetsprops.children.md @@ -14,5 +14,5 @@ children?: ReactElement[] | ReactElement | undefined | null; ## Remarks -Supported components include [StandardFacet()](./search-ui-react.standardfacet.md). +Supported components include [StandardFacet()](./search-ui-react.standardfacet.md), [NumericalFacet()](./search-ui-react.numericalfacet.md). diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 26bc33e2f..5f48f36a1 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -26,6 +26,7 @@ | [isCtaData(data)](./search-ui-react.isctadata.md) | Type guard for CtaData. | | [LocationBias({ geolocationOptions, customCssClasses })](./search-ui-react.locationbias.md) | A React Component which displays and collects location information in order to bias searches. | | [MapboxMap({ mapboxAccessToken, mapboxOptions, PinComponent, getCoordinate, onDrag })](./search-ui-react.mapboxmap.md) | A component that renders a map with markers to show result locations using Mapbox GL. | +| [NumericalFacet(props)](./search-ui-react.numericalfacet.md) | A component that displays a single numerical facet. Use this to override the default rendering. | | [NumericalFacets({ searchOnChange, includedFieldIds, getFilterDisplayName, inputPrefix, customCssClasses, ...filterGroupProps })](./search-ui-react.numericalfacets.md) | A component that displays numerical facets applicable to the current vertical search. | | [Pagination(props)](./search-ui-react.pagination.md) | Renders a component that divide a series of vertical results into chunks across multiple pages and enable user to navigate between those pages. | | [renderHighlightedValue(highlightedValueOrString, customCssClasses)](./search-ui-react.renderhighlightedvalue.md) | Renders a HighlightedValue with highlighting based on its matchedSubstrings. | @@ -78,6 +79,7 @@ | [LocationBiasCssClasses](./search-ui-react.locationbiascssclasses.md) | The CSS class interface for the [LocationBias()](./search-ui-react.locationbias.md) component. | | [LocationBiasProps](./search-ui-react.locationbiasprops.md) | The props for the [LocationBias()](./search-ui-react.locationbias.md) component. | | [MapboxMapProps](./search-ui-react.mapboxmapprops.md) | Props for the [MapboxMap()](./search-ui-react.mapboxmap.md) component. The type param "T" represents the type of "rawData" field of the results use in the map. | +| [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) | Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. | | [NumericalFacetsCssClasses](./search-ui-react.numericalfacetscssclasses.md) | The CSS class interface for [NumericalFacets()](./search-ui-react.numericalfacets.md). | | [NumericalFacetsProps](./search-ui-react.numericalfacetsprops.md) | Props for the [NumericalFacets()](./search-ui-react.numericalfacets.md) component. | | [OnSelectParams](./search-ui-react.onselectparams.md) | The parameters that are passed into [FilterSearchProps.onSelect](./search-ui-react.filtersearchprops.onselect.md). | diff --git a/docs/search-ui-react.numericalfacet.md b/docs/search-ui-react.numericalfacet.md new file mode 100644 index 000000000..d192a66ed --- /dev/null +++ b/docs/search-ui-react.numericalfacet.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacet](./search-ui-react.numericalfacet.md) + +## NumericalFacet() function + +A component that displays a single numerical facet. Use this to override the default rendering. + +Signature: + +```typescript +export declare function NumericalFacet(props: NumericalFacetProps): null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) | [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) | + +Returns: + +null + +ReactElement + diff --git a/docs/search-ui-react.numericalfacetprops.customcssclasses.md b/docs/search-ui-react.numericalfacetprops.customcssclasses.md new file mode 100644 index 000000000..602d8a32c --- /dev/null +++ b/docs/search-ui-react.numericalfacetprops.customcssclasses.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) > [customCssClasses](./search-ui-react.numericalfacetprops.customcssclasses.md) + +## NumericalFacetProps.customCssClasses property + +CSS classes for customizing the component styling. + +Signature: + +```typescript +customCssClasses?: FilterGroupCssClasses & RangeInputCssClasses; +``` diff --git a/docs/search-ui-react.numericalfacetprops.getfilterdisplayname.md b/docs/search-ui-react.numericalfacetprops.getfilterdisplayname.md new file mode 100644 index 000000000..6f4034e70 --- /dev/null +++ b/docs/search-ui-react.numericalfacetprops.getfilterdisplayname.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) > [getFilterDisplayName](./search-ui-react.numericalfacetprops.getfilterdisplayname.md) + +## NumericalFacetProps.getFilterDisplayName property + +Returns the filter's display name based on the range values which is used when the filter is displayed by other components such as AppliedFilters. + +Signature: + +```typescript +getFilterDisplayName?: (value: NumberRangeValue) => string; +``` + +## Remarks + +By default, the displayName separates the range with a dash such as '10 - 20'. If the range is unbounded, it will display as 'Up to 20' or 'Over 10'. + diff --git a/docs/search-ui-react.numericalfacetprops.inputprefix.md b/docs/search-ui-react.numericalfacetprops.inputprefix.md new file mode 100644 index 000000000..50b0b3877 --- /dev/null +++ b/docs/search-ui-react.numericalfacetprops.inputprefix.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) > [inputPrefix](./search-ui-react.numericalfacetprops.inputprefix.md) + +## NumericalFacetProps.inputPrefix property + +An optional element which renders in front of the input text. + +Signature: + +```typescript +inputPrefix?: JSX.Element; +``` diff --git a/docs/search-ui-react.numericalfacetprops.md b/docs/search-ui-react.numericalfacetprops.md new file mode 100644 index 000000000..0fc6c4b7f --- /dev/null +++ b/docs/search-ui-react.numericalfacetprops.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) + +## NumericalFacetProps interface + +Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. + +Signature: + +```typescript +export interface NumericalFacetProps extends StandardFacetProps +``` +Extends: [StandardFacetProps](./search-ui-react.standardfacetprops.md) + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [customCssClasses?](./search-ui-react.numericalfacetprops.customcssclasses.md) | [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) & [RangeInputCssClasses](./search-ui-react.rangeinputcssclasses.md) | (Optional) CSS classes for customizing the component styling. | +| [getFilterDisplayName?](./search-ui-react.numericalfacetprops.getfilterdisplayname.md) | (value: NumberRangeValue) => string | (Optional) Returns the filter's display name based on the range values which is used when the filter is displayed by other components such as AppliedFilters. | +| [inputPrefix?](./search-ui-react.numericalfacetprops.inputprefix.md) | JSX.Element | (Optional) An optional element which renders in front of the input text. | +| [showOptionCounts?](./search-ui-react.numericalfacetprops.showoptioncounts.md) | boolean | (Optional) Whether or not to show the option counts for each filter. Defaults to false. | + diff --git a/docs/search-ui-react.numericalfacetprops.showoptioncounts.md b/docs/search-ui-react.numericalfacetprops.showoptioncounts.md new file mode 100644 index 000000000..8f510813b --- /dev/null +++ b/docs/search-ui-react.numericalfacetprops.showoptioncounts.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) > [showOptionCounts](./search-ui-react.numericalfacetprops.showoptioncounts.md) + +## NumericalFacetProps.showOptionCounts property + +Whether or not to show the option counts for each filter. Defaults to false. + +Signature: + +```typescript +showOptionCounts?: boolean; +``` diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index 82b5d8ffb..08c3d8c9d 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -203,7 +203,7 @@ export function executeAutocomplete(searchActions: SearchActions): Promise; // @public -export type FacetProps = StandardFacetProps; +export type FacetProps = StandardFacetProps | NumericalFacetProps; // @public export function Facets(props: FacetsProps): JSX.Element; @@ -418,6 +418,17 @@ export interface MapboxMapProps { PinComponent?: PinComponent; } +// @public +export function NumericalFacet(props: NumericalFacetProps): null; + +// @public +export interface NumericalFacetProps extends StandardFacetProps { + customCssClasses?: FilterGroupCssClasses & RangeInputCssClasses; + getFilterDisplayName?: (value: NumberRangeValue) => string; + inputPrefix?: JSX.Element; + showOptionCounts?: boolean; +} + // @public export function NumericalFacets({ searchOnChange, includedFieldIds, getFilterDisplayName, inputPrefix, customCssClasses, ...filterGroupProps }: NumericalFacetsProps): JSX.Element; diff --git a/src/components/FacetProps.tsx b/src/components/FacetProps.tsx index de5de039a..018b4356d 100644 --- a/src/components/FacetProps.tsx +++ b/src/components/FacetProps.tsx @@ -1,6 +1,8 @@ import { DisplayableFacetOption } from '@yext/search-headless-react'; import { FilterGroupCssClasses } from './FilterGroup'; import { ReactElement } from 'react'; +import { NumberRangeValue } from '@yext/search-headless-react'; +import { RangeInputCssClasses } from './Filters'; /** * The CSS class interface for {@link Facets}. @@ -27,7 +29,7 @@ export interface FacetsProps { /** The custom facet components that will override the default rendering. * * @remarks - * Supported components include {@link StandardFacet}. + * Supported components include {@link StandardFacet}, {@link NumericalFacet}. */ children?: ReactElement[] | ReactElement | undefined | null } @@ -56,9 +58,34 @@ export interface StandardFacetProps { customCssClasses?: FilterGroupCssClasses } +/** + * Props for the {@link StandardFacet} component. + * + * @public + */ +export interface NumericalFacetProps extends StandardFacetProps { + /** Whether or not to show the option counts for each filter. Defaults to false. */ + showOptionCounts?: boolean, + /** CSS classes for customizing the component styling. */ + customCssClasses?: FilterGroupCssClasses & RangeInputCssClasses, + /** + * Returns the filter's display name based on the range values which is used when the filter + * is displayed by other components such as AppliedFilters. + * + * @remarks + * By default, the displayName separates the range with a dash such as '10 - 20'. + * If the range is unbounded, it will display as 'Up to 20' or 'Over 10'. + */ + getFilterDisplayName?: (value: NumberRangeValue) => string, + /** + * An optional element which renders in front of the input text. + */ + inputPrefix?: JSX.Element +} + /** * Props for a single facet component. * * @public */ -export type FacetProps = StandardFacetProps; +export type FacetProps = StandardFacetProps | NumericalFacetProps; diff --git a/src/components/Facets.tsx b/src/components/Facets.tsx index d03ebf5bb..348b95498 100644 --- a/src/components/Facets.tsx +++ b/src/components/Facets.tsx @@ -2,13 +2,13 @@ import { FacetsProvider } from './Filters'; import { StandardFacetContent } from './StandardFacetContent'; import { FacetProps, - FacetsProps, + FacetsProps, NumericalFacetProps, StandardFacetProps } from './FacetProps'; import { isNumericalFacet, isStringFacet } from '../utils/filterutils'; import { FilterDivider } from './FilterDivider'; import { Fragment, ReactElement } from 'react'; -import { NumericalFacets } from './NumericalFacets'; +import { NumericalFacetContent } from './NumericalFacetContent'; /** @internal */ enum FacetType { @@ -26,7 +26,7 @@ enum FacetType { * and {@link HierarchicalFacets} components can be used instead for more control over facet * configuration. * - * To override a single facet, use {@link StandardFacet}. + * To override a single facet, use {@link StandardFacet} or {@link NumericalFacet}. * * @param props - {@link FacetsProps} * @returns A React component for facets @@ -68,16 +68,12 @@ export function Facets(props: FacetsProps) { let facetComponent: ReactElement; switch (facetType) { case FacetType.NUMERICAL: - facetComponent =
; + facetComponent = (); break; case FacetType.STANDARD: // fall through default: - facetComponent = ( - ); + facetComponent = (); } return ( @@ -89,7 +85,6 @@ export function Facets(props: FacetsProps) { }) } -
); } @@ -103,6 +98,15 @@ export function Facets(props: FacetsProps) { */ export function StandardFacet(props: StandardFacetProps) { return null; } +/** + * A component that displays a single numerical facet. Use this to override the default rendering. + * + * @param props - {@link NumericalFacetProps} + * @returns ReactElement + * @public + */ +export function NumericalFacet(props: NumericalFacetProps) { return null; } + /** * Returns the type of the facet based on the props. * @param elementType - string @@ -112,7 +116,9 @@ export function StandardFacet(props: StandardFacetProps) { return null; } */ export function getFacetTypeFromReactElementType(elementType: string) { switch (elementType) { - case StandardFacet.toString(): + case NumericalFacet.name.toString(): + return FacetType.NUMERICAL; + case StandardFacet.name.toString(): // fall through default: return FacetType.STANDARD; diff --git a/src/components/NumericalFacetContent.tsx b/src/components/NumericalFacetContent.tsx new file mode 100644 index 000000000..9583e4b9b --- /dev/null +++ b/src/components/NumericalFacetContent.tsx @@ -0,0 +1,50 @@ +import { FilterGroup } from './FilterGroup'; +import { DisplayableFacet } from '@yext/search-headless-react'; +import { NumericalFacetProps } from './FacetProps'; +import { RangeInput } from './Filters'; + +const DEFAULT_RANGE_INPUT_PREFIX = <>$; + +/** + * A component that displays the content of a numerical facet. + * + * @param props - props to render the component + * @returns A React component for the content of a standard facet + * + * @internal + */ +export function NumericalFacetContent({ + fieldId, + customCssClasses, + transformOptions, + getFilterDisplayName, + facet, + label = '', + showMoreLimit = 10, + showOptionCounts = false, + inputPrefix = DEFAULT_RANGE_INPUT_PREFIX, + ...filterGroupProps +}: NumericalFacetProps & { facet: DisplayableFacet }) { + const options = facet.options || []; + const transformedOptions = transformOptions ? (transformOptions(options) || []) : options; + + return ( + { + return showOptionCounts ? { ...o, resultsCount: o.count } : o; + })} + title={label} + customCssClasses={customCssClasses} + showMoreLimit={showMoreLimit} + searchable={facet?.options.length > showMoreLimit} + {...filterGroupProps} + > + + + ); +} diff --git a/src/components/index.ts b/src/components/index.ts index ba055d85a..6216c3b54 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -127,14 +127,16 @@ export { export { Facets, - StandardFacet + StandardFacet, + NumericalFacet } from './Facets'; export { FacetsCssClasses, FacetsProps, FacetProps, - StandardFacetProps + StandardFacetProps, + NumericalFacetProps } from './FacetProps'; export { diff --git a/tests/components/Facets.test.tsx b/tests/components/Facets.test.tsx index e1b70fde3..c38c5bafc 100644 --- a/tests/components/Facets.test.tsx +++ b/tests/components/Facets.test.tsx @@ -5,7 +5,7 @@ import { } from '@yext/search-headless-react'; import { mockAnswersHooks, mockAnswersState } from '../__utils__/mocks'; import { DisplayableFacets } from '../__fixtures__/data/filters'; -import { Facets, StandardFacet, StandardFacetProps } from '../../src'; +import { Facets, StandardFacet, StandardFacetProps, NumericalFacet, NumericalFacetProps } from '../../src'; import { getOptionLabelText } from '../__utils__/facets'; import { DisplayableFacetOption } from '@yext/search-core'; @@ -116,4 +116,27 @@ describe('Facets', () => { `my ${regularFilter.options[0].displayName} (${regularFilter.options[0].count})`)) .toBeDefined(); }); + + it('Properly renders an override numerical facet if present', () => { + const mockTransformOptions = (options: DisplayableFacetOption[]) => + options.map(option => ({ ...option, displayName: `Price is ${option.displayName}` })); + + const overrideFieldId = 'price'; + const overrideLabel = 'My Price'; + const props: NumericalFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + transformOptions: mockTransformOptions, + }; + + render( + + + ); + const regularFilter = DisplayableFacets[1]; + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.queryByText(regularFilter.displayName)).toBeNull(); + expect(screen.getByText(`Price is ${regularFilter.options[0].displayName}`)).toBeDefined(); + }); }); \ No newline at end of file diff --git a/tests/components/NumericalFacet.stories.tsx b/tests/components/NumericalFacet.stories.tsx new file mode 100644 index 000000000..ef47eff76 --- /dev/null +++ b/tests/components/NumericalFacet.stories.tsx @@ -0,0 +1,37 @@ +import { ComponentMeta, Story } from '@storybook/react'; +import { Facets, NumericalFacetProps, NumericalFacet } from '../../src'; +import { SearchHeadlessContext, State } from '@yext/search-headless-react'; +import { generateMockedHeadless } from '../__fixtures__/search-headless'; +import { RecursivePartial } from '../__utils__/mocks'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; + +const meta: ComponentMeta = { + title: 'Facets', + component: Facets +}; +export default meta; + +const mockedHeadlessState: RecursivePartial = { + filters: { + facets: [ + ...DisplayableFacets, + createHierarchicalFacet([ + 'food', + 'food > fruit', + { value: 'food > fruit > banana', selected: true }, + 'food > fruit > apple', + ]) + ] + } +}; + +export const Primary: Story = (args) => { + return ( + + + + + + ); +}; \ No newline at end of file diff --git a/tests/components/StandardFacet.stories.tsx b/tests/components/StandardFacet.stories.tsx index cbb3255c8..ef47eff76 100644 --- a/tests/components/StandardFacet.stories.tsx +++ b/tests/components/StandardFacet.stories.tsx @@ -1,16 +1,37 @@ import { ComponentMeta, Story } from '@storybook/react'; -import { Facets, StandardFacetProps, StandardFacet } from '../../src'; +import { Facets, NumericalFacetProps, NumericalFacet } from '../../src'; +import { SearchHeadlessContext, State } from '@yext/search-headless-react'; +import { generateMockedHeadless } from '../__fixtures__/search-headless'; +import { RecursivePartial } from '../__utils__/mocks'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; -const meta: ComponentMeta = { - title: 'StandardFacet', - component: StandardFacet +const meta: ComponentMeta = { + title: 'Facets', + component: Facets }; export default meta; -export const Primary: Story = (args) => { +const mockedHeadlessState: RecursivePartial = { + filters: { + facets: [ + ...DisplayableFacets, + createHierarchicalFacet([ + 'food', + 'food > fruit', + { value: 'food > fruit > banana', selected: true }, + 'food > fruit > apple', + ]) + ] + } +}; + +export const Primary: Story = (args) => { return ( - - - + + + + + ); }; \ No newline at end of file From e9183f31a659807a6d3eeff21a628cc29d00941b Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Tue, 23 May 2023 12:25:59 -0400 Subject: [PATCH 15/23] Add HierarchicalFacet (#373) * Add HierarchicalFacet This PR includes the following changes: - Add support for HierarchicalFacet in Facets. - Add unit tests for singular facet components. - Add Facets to PeoplePage. J=BACK-2223 TEST=auto,manual Ran unit tests. Manually checked the behavior of Facets on PeoplePage. --- .github/workflows/coverage.yml | 2 +- .storybook/preview.js | 4 + docs/search-ui-react.facetprops.md | 4 +- docs/search-ui-react.facets.md | 4 +- ...h-ui-react.facetsprops.excludedfieldids.md | 2 +- ...-react.facetsprops.hierarchicalfieldids.md | 13 ++ docs/search-ui-react.facetsprops.md | 4 +- ...ui-react.facetsprops.onlyrenderchildren.md | 13 ++ docs/search-ui-react.hierarchicalfacet.md | 26 +++ ...hierarchicalfacetprops.customcssclasses.md | 13 ++ ...-react.hierarchicalfacetprops.delimiter.md | 13 ++ .../search-ui-react.hierarchicalfacetprops.md | 24 +++ ...ct.hierarchicalfacetprops.showmorelimit.md | 13 ++ ...hierarchicalfacetprops.transformoptions.md | 13 ++ docs/search-ui-react.hierarchicalfacets.md | 5 + docs/search-ui-react.md | 2 + docs/search-ui-react.numericalfacets.md | 5 + docs/search-ui-react.standardfacetprops.md | 2 +- ...-react.standardfacetprops.showmorelimit.md | 2 +- etc/search-ui-react.api.md | 18 +- src/components/FacetProps.tsx | 49 ++++- src/components/FacetTiltle.tsx | 47 +++++ src/components/Facets.tsx | 157 +++++++++++----- src/components/FilterGroup.tsx | 26 +-- .../Filters/HierarchicalFacetDisplay.tsx | 4 +- src/components/HierarchicalFacetContent.tsx | 53 ++++++ src/components/HierarchicalFacets.tsx | 1 + src/components/NumericalFacetContent.tsx | 6 +- src/components/NumericalFacets.tsx | 1 + src/components/StandardFacetContent.tsx | 6 +- src/components/index.ts | 6 +- src/utils/filterutils.tsx | 4 +- test-site/package-lock.json | 8 +- test-site/src/pages/PeoplePage.tsx | 15 +- tests/__fixtures__/data/filters.ts | 9 +- tests/__utils__/facets.ts | 17 +- tests/components/Facets.test.tsx | 139 +++++++++++--- .../components/HierarchicalFacet.stories.tsx | 37 ++++ .../HierarchicalFacetContent.test.tsx | 141 ++++++++++++++ .../components/NumericalFacetContent.test.tsx | 176 ++++++++++++++++++ tests/components/StandardFacet.stories.tsx | 10 +- .../components/StandardFacetContent.test.tsx | 120 ++++++++++++ tests/components/StandardFacets.stories.tsx | 2 +- tests/components/StandardFacets.test.tsx | 32 +--- 44 files changed, 1098 insertions(+), 150 deletions(-) create mode 100644 docs/search-ui-react.facetsprops.hierarchicalfieldids.md create mode 100644 docs/search-ui-react.facetsprops.onlyrenderchildren.md create mode 100644 docs/search-ui-react.hierarchicalfacet.md create mode 100644 docs/search-ui-react.hierarchicalfacetprops.customcssclasses.md create mode 100644 docs/search-ui-react.hierarchicalfacetprops.delimiter.md create mode 100644 docs/search-ui-react.hierarchicalfacetprops.md create mode 100644 docs/search-ui-react.hierarchicalfacetprops.showmorelimit.md create mode 100644 docs/search-ui-react.hierarchicalfacetprops.transformoptions.md create mode 100644 src/components/FacetTiltle.tsx create mode 100644 src/components/HierarchicalFacetContent.tsx create mode 100644 tests/components/HierarchicalFacet.stories.tsx create mode 100644 tests/components/HierarchicalFacetContent.test.tsx create mode 100644 tests/components/NumericalFacetContent.test.tsx create mode 100644 tests/components/StandardFacetContent.test.tsx diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4ead6d09d..3eab2d352 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,5 +1,5 @@ # This workflow will run our tests, generate an lcov code coverage file, -# and send that coverage to Coveralls +# and send that coverage to Coveralls name: Code Coverage diff --git a/.storybook/preview.js b/.storybook/preview.js index f0e0b4f44..aba45a122 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -27,6 +27,10 @@ export const parameters = { 'DirectAnswer', 'FilterSearch', 'StaticFilters', + 'Facets', + 'StandardFacet', + 'NumericalFacet', + 'HierarchicalFacet', 'StandardFacets', 'NumericalFacets', 'HierarchicalFacets', diff --git a/docs/search-ui-react.facetprops.md b/docs/search-ui-react.facetprops.md index c5429d55f..c0f1e3594 100644 --- a/docs/search-ui-react.facetprops.md +++ b/docs/search-ui-react.facetprops.md @@ -9,7 +9,7 @@ Props for a single facet component. Signature: ```typescript -export declare type FacetProps = StandardFacetProps | NumericalFacetProps; +export declare type FacetProps = StandardFacetProps | NumericalFacetProps | HierarchicalFacetProps; ``` -References: [StandardFacetProps](./search-ui-react.standardfacetprops.md), [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) +References: [StandardFacetProps](./search-ui-react.standardfacetprops.md), [NumericalFacetProps](./search-ui-react.numericalfacetprops.md), [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) diff --git a/docs/search-ui-react.facets.md b/docs/search-ui-react.facets.md index e24bc7ad3..09a361d52 100644 --- a/docs/search-ui-react.facets.md +++ b/docs/search-ui-react.facets.md @@ -26,7 +26,5 @@ A React component for facets ## Remarks -This component is a quick way of getting facets on the page, and it will render standard facets, numerical facets, and hierarchical facets. The [StandardFacets()](./search-ui-react.standardfacets.md), [NumericalFacets()](./search-ui-react.numericalfacets.md), and [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) components can be used instead for more control over facet configuration. - -To override a single facet, use [StandardFacet()](./search-ui-react.standardfacet.md) or [NumericalFacet()](./search-ui-react.numericalfacet.md). +This component is a quick way of getting facets on the page, and it will render standard facets, numerical facets, and hierarchical facets. The [StandardFacet()](./search-ui-react.standardfacet.md), [NumericalFacet()](./search-ui-react.numericalfacet.md), and [HierarchicalFacet()](./search-ui-react.hierarchicalfacet.md) components can be used to override the default facet configuration. diff --git a/docs/search-ui-react.facetsprops.excludedfieldids.md b/docs/search-ui-react.facetsprops.excludedfieldids.md index ea330c1e5..35c5f2335 100644 --- a/docs/search-ui-react.facetsprops.excludedfieldids.md +++ b/docs/search-ui-react.facetsprops.excludedfieldids.md @@ -4,7 +4,7 @@ ## FacetsProps.excludedFieldIds property -List of filter ids that should not be displayed. +List of field ids that should not be displayed. Signature: diff --git a/docs/search-ui-react.facetsprops.hierarchicalfieldids.md b/docs/search-ui-react.facetsprops.hierarchicalfieldids.md new file mode 100644 index 000000000..615e21cf1 --- /dev/null +++ b/docs/search-ui-react.facetsprops.hierarchicalfieldids.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [hierarchicalFieldIds](./search-ui-react.facetsprops.hierarchicalfieldids.md) + +## FacetsProps.hierarchicalFieldIds property + +List of field ids that should be rendered as hierarchical facets. + +Signature: + +```typescript +hierarchicalFieldIds?: string[]; +``` diff --git a/docs/search-ui-react.facetsprops.md b/docs/search-ui-react.facetsprops.md index 6a2ac75a1..926b794a7 100644 --- a/docs/search-ui-react.facetsprops.md +++ b/docs/search-ui-react.facetsprops.md @@ -18,6 +18,8 @@ export interface FacetsProps | --- | --- | --- | | [children?](./search-ui-react.facetsprops.children.md) | ReactElement\[\] \| ReactElement \| undefined \| null | (Optional) The custom facet components that will override the default rendering. | | [customCssClasses?](./search-ui-react.facetsprops.customcssclasses.md) | [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | (Optional) CSS classes for customizing the component styling. | -| [excludedFieldIds?](./search-ui-react.facetsprops.excludedfieldids.md) | string\[\] | (Optional) List of filter ids that should not be displayed. | +| [excludedFieldIds?](./search-ui-react.facetsprops.excludedfieldids.md) | string\[\] | (Optional) List of field ids that should not be displayed. | +| [hierarchicalFieldIds?](./search-ui-react.facetsprops.hierarchicalfieldids.md) | string\[\] | (Optional) List of field ids that should be rendered as hierarchical facets. | +| [onlyRenderChildren?](./search-ui-react.facetsprops.onlyrenderchildren.md) | boolean | (Optional) If set to true, only the facets specified in the children are rendered. If set to false, all facets are rendered with the ones specified in the children overridden. Default to false. | | [searchOnChange?](./search-ui-react.facetsprops.searchonchange.md) | boolean | (Optional) Whether or not a search is automatically run when a filter is selected. Defaults to true. | diff --git a/docs/search-ui-react.facetsprops.onlyrenderchildren.md b/docs/search-ui-react.facetsprops.onlyrenderchildren.md new file mode 100644 index 000000000..f836cdc32 --- /dev/null +++ b/docs/search-ui-react.facetsprops.onlyrenderchildren.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [FacetsProps](./search-ui-react.facetsprops.md) > [onlyRenderChildren](./search-ui-react.facetsprops.onlyrenderchildren.md) + +## FacetsProps.onlyRenderChildren property + +If set to true, only the facets specified in the children are rendered. If set to false, all facets are rendered with the ones specified in the children overridden. Default to false. + +Signature: + +```typescript +onlyRenderChildren?: boolean; +``` diff --git a/docs/search-ui-react.hierarchicalfacet.md b/docs/search-ui-react.hierarchicalfacet.md new file mode 100644 index 000000000..448f86662 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacet.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacet](./search-ui-react.hierarchicalfacet.md) + +## HierarchicalFacet() function + +A component that displays a single hierarchical facet, in a tree level structure, applicable to the current vertical search. Use this to override the default rendering. + +Signature: + +```typescript +export declare function HierarchicalFacet(props: HierarchicalFacetProps): null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| props | [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) | [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) | + +Returns: + +null + +ReactElement + diff --git a/docs/search-ui-react.hierarchicalfacetprops.customcssclasses.md b/docs/search-ui-react.hierarchicalfacetprops.customcssclasses.md new file mode 100644 index 000000000..95f63b014 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacetprops.customcssclasses.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) > [customCssClasses](./search-ui-react.hierarchicalfacetprops.customcssclasses.md) + +## HierarchicalFacetProps.customCssClasses property + +CSS classes for customizing the component styling. + +Signature: + +```typescript +customCssClasses?: HierarchicalFacetCustomCssClasses; +``` diff --git a/docs/search-ui-react.hierarchicalfacetprops.delimiter.md b/docs/search-ui-react.hierarchicalfacetprops.delimiter.md new file mode 100644 index 000000000..571bc3323 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacetprops.delimiter.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) > [delimiter](./search-ui-react.hierarchicalfacetprops.delimiter.md) + +## HierarchicalFacetProps.delimiter property + +The delimiter for determining facet hierarchies, defaults to ">". + +Signature: + +```typescript +delimiter?: string; +``` diff --git a/docs/search-ui-react.hierarchicalfacetprops.md b/docs/search-ui-react.hierarchicalfacetprops.md new file mode 100644 index 000000000..1ec8e98f5 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacetprops.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) + +## HierarchicalFacetProps interface + +Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. + +Signature: + +```typescript +export interface HierarchicalFacetProps extends Omit +``` +Extends: Omit<[StandardFacetProps](./search-ui-react.standardfacetprops.md), 'transformOptions' \| 'showOptionCounts'> + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [customCssClasses?](./search-ui-react.hierarchicalfacetprops.customcssclasses.md) | HierarchicalFacetCustomCssClasses | (Optional) CSS classes for customizing the component styling. | +| [delimiter?](./search-ui-react.hierarchicalfacetprops.delimiter.md) | string | (Optional) The delimiter for determining facet hierarchies, defaults to ">". | +| [showMoreLimit?](./search-ui-react.hierarchicalfacetprops.showmorelimit.md) | number | (Optional) The maximum number of options to render before displaying the "Show more/less" button. Defaults to 4. | +| [transformOptions?](./search-ui-react.hierarchicalfacetprops.transformoptions.md) | (options: DisplayableFacetOption\[\]) => DisplayableFacetOption\[\] | (Optional) A function to transform facet's options. The returned options need to be delimited to keep the hierarchy. | + diff --git a/docs/search-ui-react.hierarchicalfacetprops.showmorelimit.md b/docs/search-ui-react.hierarchicalfacetprops.showmorelimit.md new file mode 100644 index 000000000..6469cb024 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacetprops.showmorelimit.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) > [showMoreLimit](./search-ui-react.hierarchicalfacetprops.showmorelimit.md) + +## HierarchicalFacetProps.showMoreLimit property + +The maximum number of options to render before displaying the "Show more/less" button. Defaults to 4. + +Signature: + +```typescript +showMoreLimit?: number; +``` diff --git a/docs/search-ui-react.hierarchicalfacetprops.transformoptions.md b/docs/search-ui-react.hierarchicalfacetprops.transformoptions.md new file mode 100644 index 000000000..8a789b5c9 --- /dev/null +++ b/docs/search-ui-react.hierarchicalfacetprops.transformoptions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) > [transformOptions](./search-ui-react.hierarchicalfacetprops.transformoptions.md) + +## HierarchicalFacetProps.transformOptions property + +A function to transform facet's options. The returned options need to be delimited to keep the hierarchy. + +Signature: + +```typescript +transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[]; +``` diff --git a/docs/search-ui-react.hierarchicalfacets.md b/docs/search-ui-react.hierarchicalfacets.md index f2f3f74aa..fff8a6796 100644 --- a/docs/search-ui-react.hierarchicalfacets.md +++ b/docs/search-ui-react.hierarchicalfacets.md @@ -4,6 +4,11 @@ ## HierarchicalFacets() function +> Warning: This API is now obsolete. +> +> Use [Facets()](./search-ui-react.facets.md) instead. +> + A component that displays hierarchical facets, in a tree level structure, applicable to the current vertical search. Signature: diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 5f48f36a1..0bd986785 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -22,6 +22,7 @@ | [Geolocation\_2({ geolocationOptions, radius, label, GeolocationIcon, handleClick, customCssClasses, })](./search-ui-react.geolocation_2.md) | A React Component which collects location information to create a location filter and perform a new search. | | [getSearchIntents(searchActions)](./search-ui-react.getsearchintents.md) | Get search intents of the current query stored in headless using autocomplete request. | | [getUserLocation(geolocationOptions)](./search-ui-react.getuserlocation.md) | Retrieves user's location using navigator.geolocation API. | +| [HierarchicalFacet(props)](./search-ui-react.hierarchicalfacet.md) | A component that displays a single hierarchical facet, in a tree level structure, applicable to the current vertical search. Use this to override the default rendering. | | [HierarchicalFacets({ searchOnChange, collapsible, defaultExpanded, includedFieldIds, customCssClasses, delimiter, showMoreLimit })](./search-ui-react.hierarchicalfacets.md) | A component that displays hierarchical facets, in a tree level structure, applicable to the current vertical search. | | [isCtaData(data)](./search-ui-react.isctadata.md) | Type guard for CtaData. | | [LocationBias({ geolocationOptions, customCssClasses })](./search-ui-react.locationbias.md) | A React Component which displays and collects location information in order to bias searches. | @@ -73,6 +74,7 @@ | [GeolocationCssClasses](./search-ui-react.geolocationcssclasses.md) | The CSS class interface for the Geolocation component. | | [GeolocationProps](./search-ui-react.geolocationprops.md) | The props for the Geolocation component. | | [HierarchicalFacetDisplayCssClasses](./search-ui-react.hierarchicalfacetdisplaycssclasses.md) | The CSS class interface for HierarchicalFacetDisplay. | +| [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md) | Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. | | [HierarchicalFacetsCssClasses](./search-ui-react.hierarchicalfacetscssclasses.md) | The CSS class interface for [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md). | | [HierarchicalFacetsProps](./search-ui-react.hierarchicalfacetsprops.md) | Props for the [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) component. | | [HighlightedValueCssClasses](./search-ui-react.highlightedvaluecssclasses.md) | The CSS class interface for [renderHighlightedValue()](./search-ui-react.renderhighlightedvalue.md). | diff --git a/docs/search-ui-react.numericalfacets.md b/docs/search-ui-react.numericalfacets.md index c9eca4523..290c485a0 100644 --- a/docs/search-ui-react.numericalfacets.md +++ b/docs/search-ui-react.numericalfacets.md @@ -4,6 +4,11 @@ ## NumericalFacets() function +> Warning: This API is now obsolete. +> +> Use [Facets()](./search-ui-react.facets.md) instead. +> + A component that displays numerical facets applicable to the current vertical search. Signature: diff --git a/docs/search-ui-react.standardfacetprops.md b/docs/search-ui-react.standardfacetprops.md index 62e93d797..da208c1ef 100644 --- a/docs/search-ui-react.standardfacetprops.md +++ b/docs/search-ui-react.standardfacetprops.md @@ -21,7 +21,7 @@ export interface StandardFacetProps | [defaultExpanded?](./search-ui-react.standardfacetprops.defaultexpanded.md) | boolean | (Optional) If the filter group is collapsible, whether or not it should start out expanded. Defaults to true. | | [fieldId](./search-ui-react.standardfacetprops.fieldid.md) | string | The fieldId corresponding to the facet | | [label?](./search-ui-react.standardfacetprops.label.md) | string | (Optional) The label of the facet. Defaults to facet's displayName if not provided. | -| [showMoreLimit?](./search-ui-react.standardfacetprops.showmorelimit.md) | number | (Optional) Limit on the number of options to be displayed. Defaults to 10. | +| [showMoreLimit?](./search-ui-react.standardfacetprops.showmorelimit.md) | number | (Optional) The maximum number of options to render before displaying the "Show more/less" button. Defaults to 10. | | [showOptionCounts?](./search-ui-react.standardfacetprops.showoptioncounts.md) | boolean | (Optional) Whether or not to show the option counts for each filter. Defaults to true. | | [transformOptions?](./search-ui-react.standardfacetprops.transformoptions.md) | (options: DisplayableFacetOption\[\]) => DisplayableFacetOption\[\] | (Optional) A function to transform facet's options. | diff --git a/docs/search-ui-react.standardfacetprops.showmorelimit.md b/docs/search-ui-react.standardfacetprops.showmorelimit.md index 40dae1672..ec85f8dc1 100644 --- a/docs/search-ui-react.standardfacetprops.showmorelimit.md +++ b/docs/search-ui-react.standardfacetprops.showmorelimit.md @@ -4,7 +4,7 @@ ## StandardFacetProps.showMoreLimit property -Limit on the number of options to be displayed. Defaults to 10. +The maximum number of options to render before displaying the "Show more/less" button. Defaults to 10. Signature: diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index 08c3d8c9d..47265dbce 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -203,7 +203,7 @@ export function executeAutocomplete(searchActions: SearchActions): Promise; // @public -export type FacetProps = StandardFacetProps | NumericalFacetProps; +export type FacetProps = StandardFacetProps | NumericalFacetProps | HierarchicalFacetProps; // @public export function Facets(props: FacetsProps): JSX.Element; @@ -221,6 +221,8 @@ export interface FacetsProps { children?: ReactElement[] | ReactElement | undefined | null; customCssClasses?: FacetsCssClasses; excludedFieldIds?: string[]; + hierarchicalFieldIds?: string[]; + onlyRenderChildren?: boolean; searchOnChange?: boolean; } @@ -333,6 +335,9 @@ export function getSearchIntents(searchActions: SearchActions): Promise; +// @public +export function HierarchicalFacet(props: HierarchicalFacetProps): null; + // @public export interface HierarchicalFacetDisplayCssClasses { // (undocumented) @@ -354,6 +359,15 @@ export interface HierarchicalFacetDisplayCssClasses { } // @public +export interface HierarchicalFacetProps extends Omit { + // Warning: (ae-forgotten-export) The symbol "HierarchicalFacetCustomCssClasses" needs to be exported by the entry point index.d.ts + customCssClasses?: HierarchicalFacetCustomCssClasses; + delimiter?: string; + showMoreLimit?: number; + transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[]; +} + +// @public @deprecated export function HierarchicalFacets({ searchOnChange, collapsible, defaultExpanded, includedFieldIds, customCssClasses, delimiter, showMoreLimit }: HierarchicalFacetsProps): JSX.Element; // @public @@ -429,7 +443,7 @@ export interface NumericalFacetProps extends StandardFacetProps { showOptionCounts?: boolean; } -// @public +// @public @deprecated export function NumericalFacets({ searchOnChange, includedFieldIds, getFilterDisplayName, inputPrefix, customCssClasses, ...filterGroupProps }: NumericalFacetsProps): JSX.Element; // @public diff --git a/src/components/FacetProps.tsx b/src/components/FacetProps.tsx index 018b4356d..b713284ab 100644 --- a/src/components/FacetProps.tsx +++ b/src/components/FacetProps.tsx @@ -2,7 +2,7 @@ import { DisplayableFacetOption } from '@yext/search-headless-react'; import { FilterGroupCssClasses } from './FilterGroup'; import { ReactElement } from 'react'; import { NumberRangeValue } from '@yext/search-headless-react'; -import { RangeInputCssClasses } from './Filters'; +import { HierarchicalFacetDisplayCssClasses, RangeInputCssClasses } from './Filters'; /** * The CSS class interface for {@link Facets}. @@ -22,10 +22,15 @@ export interface FacetsCssClasses { export interface FacetsProps { /** Whether or not a search is automatically run when a filter is selected. Defaults to true. */ searchOnChange?: boolean, + /** If set to true, only the facets specified in the children are rendered. If set to false, all + * facets are rendered with the ones specified in the children overridden. Default to false. */ + onlyRenderChildren?: boolean, /** CSS classes for customizing the component styling. */ customCssClasses?: FacetsCssClasses, - /** List of filter ids that should not be displayed. */ + /** List of field ids that should not be displayed. */ excludedFieldIds?: string[], + /** List of field ids that should be rendered as hierarchical facets. */ + hierarchicalFieldIds?: string[], /** The custom facet components that will override the default rendering. * * @remarks @@ -52,7 +57,10 @@ export interface StandardFacetProps { defaultExpanded?: boolean, /** Whether or not to show the option counts for each filter. Defaults to true. */ showOptionCounts?: boolean, - /** Limit on the number of options to be displayed. Defaults to 10. */ + /** + * The maximum number of options to render before displaying the "Show more/less" button. + * Defaults to 10. + */ showMoreLimit?: number, /** CSS classes for customizing the component styling. */ customCssClasses?: FilterGroupCssClasses @@ -83,9 +91,42 @@ export interface NumericalFacetProps extends StandardFacetProps { inputPrefix?: JSX.Element } +/** + * Props for the {@link StandardFacet} component. + * + * @public + */ +export interface HierarchicalFacetCustomCssClasses extends HierarchicalFacetDisplayCssClasses { + /** CSS classes for customizing the title label styling. */ + titleLabel?: string +} + +/** + * Props for the {@link StandardFacet} component. + * + * @public + */ +export interface HierarchicalFacetProps extends + Omit { + /** + * A function to transform facet's options. The returned options need to be delimited to keep + * the hierarchy. + */ + transformOptions?: (options: DisplayableFacetOption[]) => DisplayableFacetOption[], + /** + * The maximum number of options to render before displaying the "Show more/less" button. + * Defaults to 4. + */ + showMoreLimit?: number, + /** CSS classes for customizing the component styling. */ + customCssClasses?: HierarchicalFacetCustomCssClasses, + /** The delimiter for determining facet hierarchies, defaults to "\>". */ + delimiter?: string +} + /** * Props for a single facet component. * * @public */ -export type FacetProps = StandardFacetProps | NumericalFacetProps; +export type FacetProps = StandardFacetProps | NumericalFacetProps | HierarchicalFacetProps; diff --git a/src/components/FacetTiltle.tsx b/src/components/FacetTiltle.tsx new file mode 100644 index 000000000..38f5022a4 --- /dev/null +++ b/src/components/FacetTiltle.tsx @@ -0,0 +1,47 @@ +import { builtInCollapsibleLabelCssClasses, CollapsibleLabel, CollapsibleLabelCssClasses } from './Filters'; +import { Fragment, useMemo } from 'react'; +import { twMerge } from '../hooks/useComposedCssClasses'; +import { FilterGroupCssClasses } from './FilterGroup'; + +/** + * Props for the {@link FacetTitle} component. + * + * @internal + */ +export interface FacetTitleProps { + /** The label to use in the title. */ + label?: string, + /** {@inheritDoc FilterGroupProps.collapsible} */ + collapsible?: boolean, + /** CSS classes for customizing the component styling. */ + customCssClasses?: FilterGroupCssClasses +} + +/** + * The tile of a facet component. + * + * @param props - props to render the component + * @returns A React component + * + * @internal + */ +export function FacetTitle({ + label, + customCssClasses, + collapsible = true, +}: FacetTitleProps) { + const collapsibleLabelCssClasses: CollapsibleLabelCssClasses = useMemo(() => { + return { + label: customCssClasses?.titleLabel + }; + }, [customCssClasses]); + + return + {collapsible + ? + : (label &&
+ {label} +
)} +
; +} diff --git a/src/components/Facets.tsx b/src/components/Facets.tsx index 348b95498..eff844327 100644 --- a/src/components/Facets.tsx +++ b/src/components/Facets.tsx @@ -2,13 +2,15 @@ import { FacetsProvider } from './Filters'; import { StandardFacetContent } from './StandardFacetContent'; import { FacetProps, - FacetsProps, NumericalFacetProps, + FacetsProps, HierarchicalFacetProps, NumericalFacetProps, StandardFacetProps } from './FacetProps'; import { isNumericalFacet, isStringFacet } from '../utils/filterutils'; import { FilterDivider } from './FilterDivider'; import { Fragment, ReactElement } from 'react'; import { NumericalFacetContent } from './NumericalFacetContent'; +import { HierarchicalFacetContent } from './HierarchicalFacetContent'; +import { DisplayableFacet } from '@yext/search-headless-react'; /** @internal */ enum FacetType { @@ -22,11 +24,8 @@ enum FacetType { * * @remarks * This component is a quick way of getting facets on the page, and it will render standard facets, - * numerical facets, and hierarchical facets. The {@link StandardFacets}, {@link NumericalFacets}, - * and {@link HierarchicalFacets} components can be used instead for more control over facet - * configuration. - * - * To override a single facet, use {@link StandardFacet} or {@link NumericalFacet}. + * numerical facets, and hierarchical facets. The {@link StandardFacet}, {@link NumericalFacet}, + * and {@link HierarchicalFacet} components can be used to override the default facet configuration. * * @param props - {@link FacetsProps} * @returns A React component for facets @@ -34,55 +33,91 @@ enum FacetType { * @public */ export function Facets(props: FacetsProps) { - const { searchOnChange, children, customCssClasses = {} } = props; + const { + searchOnChange, + onlyRenderChildren = false, + children, + hierarchicalFieldIds, + excludedFieldIds = [], + customCssClasses = {}, + } = props; const fieldIdToCustomFacetProps = new Map(); + const fieldIds: string[] = []; if (children) { (Array.isArray(children) ? children : [children]) - .forEach(child => fieldIdToCustomFacetProps.set(child.props.fieldId, child)); + .filter(child => child?.props?.fieldId) + .forEach(child => { + fieldIdToCustomFacetProps.set(child.props.fieldId, child); + fieldIds.push(child.props.fieldId); + }); } return (
- {facets => facets - .map((facet, i) => { - let facetType: FacetType = FacetType.STANDARD; - let facetProps: FacetProps = { - fieldId: facet.fieldId, - label: facet.displayName, - }; - if (fieldIdToCustomFacetProps.has(facet.fieldId)) { - const customFacetElement: ReactElement = fieldIdToCustomFacetProps.get(facet.fieldId); - facetProps = customFacetElement.props; - facetType = getFacetTypeFromReactElementType( - (typeof customFacetElement.type === 'function') ? customFacetElement.type.name : ''); - } else { - if (isStringFacet(facet)) { - facetType = FacetType.STANDARD; - } else if (isNumericalFacet(facet)) { - facetType = FacetType.NUMERICAL; + {facets => { + if (!facets || !facets.length) { + return; + } + + if (!onlyRenderChildren) { + facets.forEach(facet => { + if (!fieldIds.includes(facet.fieldId)) { + fieldIds.push(facet.fieldId); + } + }); + } + + const fieldIdToFacet = new Map(); + facets.forEach(facet => fieldIdToFacet.set(facet.fieldId, facet)); + + return fieldIds + .filter(fieldId => + !excludedFieldIds.includes(fieldId) + && fieldIdToFacet.get(fieldId).options.length > 0 + && (!onlyRenderChildren || fieldIdToCustomFacetProps.has(fieldId))) + .map((fieldId, i) => { + const facet: DisplayableFacet = fieldIdToFacet.get(fieldId); + let facetType: FacetType = FacetType.STANDARD; + let facetProps: FacetProps = { + fieldId: facet.fieldId, + label: facet.displayName, + }; + if (fieldIdToCustomFacetProps.has(facet.fieldId)) { + const customFacetElement: ReactElement = + fieldIdToCustomFacetProps.get(facet.fieldId); + facetProps = { ...facetProps, ...customFacetElement.props }; + facetType = getFacetTypeFromReactElementType( + (typeof customFacetElement.type === 'function') + ? customFacetElement.type.name : ''); + } else { + facetType = getFacetTypeFromFacet(facet, hierarchicalFieldIds); } - } - let facetComponent: ReactElement; - switch (facetType) { - case FacetType.NUMERICAL: - facetComponent = (); - break; - case FacetType.STANDARD: + let facetComponent: ReactElement; + switch (facetType) { + case FacetType.NUMERICAL: + facetComponent = (); + break; + case FacetType.HIERARCHICAL: + facetComponent = (); + break; + case FacetType.STANDARD: // fall through - default: - facetComponent = (); - } + default: + facetComponent = (); + } - return ( - - {facetComponent} - {(i < facets.length - 1) && } - - ); - }) + return ( + + {facetComponent} + {(i < facets.length - 1) + && } + + ); + }); + } }
@@ -96,6 +131,7 @@ export function Facets(props: FacetsProps) { * @returns ReactElement * @public */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function StandardFacet(props: StandardFacetProps) { return null; } /** @@ -105,8 +141,20 @@ export function StandardFacet(props: StandardFacetProps) { return null; } * @returns ReactElement * @public */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function NumericalFacet(props: NumericalFacetProps) { return null; } +/** + * A component that displays a single hierarchical facet, in a tree level structure, applicable to + * the current vertical search. Use this to override the default rendering. + * + * @param props - {@link HierarchicalFacetProps} + * @returns ReactElement + * @public + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function HierarchicalFacet(props: HierarchicalFacetProps) { return null; } + /** * Returns the type of the facet based on the props. * @param elementType - string @@ -118,9 +166,34 @@ export function getFacetTypeFromReactElementType(elementType: string) { switch (elementType) { case NumericalFacet.name.toString(): return FacetType.NUMERICAL; + case HierarchicalFacet.name.toString(): + return FacetType.HIERARCHICAL; case StandardFacet.name.toString(): // fall through default: return FacetType.STANDARD; } } + +/** + * Returns the type of the facet based on facet. + * @param facet - {@link DisplayableFacet} + * @param hierarchicalFieldIds - string + * @returns {@link FacetType} + * + * @internal + */ +export function getFacetTypeFromFacet( + facet: DisplayableFacet, + hierarchicalFieldIds: string[] = [], +) { + if (hierarchicalFieldIds.includes(facet.fieldId)) { + return FacetType.HIERARCHICAL; + } else if (isStringFacet(facet)) { + return FacetType.STANDARD; + } else if (isNumericalFacet(facet)) { + return FacetType.NUMERICAL; + } + + return FacetType.STANDARD; +} diff --git a/src/components/FilterGroup.tsx b/src/components/FilterGroup.tsx index 51e8eda9c..5f8ad7c65 100644 --- a/src/components/FilterGroup.tsx +++ b/src/components/FilterGroup.tsx @@ -1,11 +1,7 @@ import { useSearchUtilities } from '@yext/search-headless-react'; import { PropsWithChildren, useMemo, useState } from 'react'; -import { twMerge } from '../hooks/useComposedCssClasses'; import { CheckboxOption, - CollapsibleLabel, - CollapsibleLabelCssClasses, - builtInCollapsibleLabelCssClasses, CollapsibleSection, FilterOptionConfig, SearchInput, @@ -13,6 +9,7 @@ import { useFilterGroupContext, CheckboxCssClasses } from './Filters'; +import { FacetTitle } from './FacetTiltle'; /** * The CSS class interface for FilterGroup. @@ -79,27 +76,16 @@ export function FilterGroup({ }; }, [customCssClasses]); - const collapsibleLabelCssClasses: CollapsibleLabelCssClasses = useMemo(() => { - return { - label: cssClasses.titleLabel - }; - }, [cssClasses]); - - function renderTitle() { - return collapsible - ? - : (title && -
- {title} -
); - } - return ( - {renderTitle()} + {searchable && } = { showMoreButton: 'ml-4 text-sm font-medium text-primary' }; -export const DEFAULT_HIERARCHICAL_DELIMITER = '>'; - /** * A HierarchicalFacetDisplay takes a `DisplayableFacet` and renders the facet in a way * to represent multiple levels of "hierarchies". diff --git a/src/components/HierarchicalFacetContent.tsx b/src/components/HierarchicalFacetContent.tsx new file mode 100644 index 000000000..6f35d4adf --- /dev/null +++ b/src/components/HierarchicalFacetContent.tsx @@ -0,0 +1,53 @@ +import { DisplayableFacet } from '@yext/search-headless-react'; +import { HierarchicalFacetProps } from './FacetProps'; +import { + CollapsibleSection, + FilterGroupProvider, + HierarchicalFacetDisplay +} from './Filters'; +import { FacetTitle } from './FacetTiltle'; + +/** + * A component that displays the content of a hierarchical facet. + * + * @param props - props to render the component + * @returns A React component for the content of a hierarchical facet + * + * @internal + */ +export function HierarchicalFacetContent({ + fieldId, + label, + transformOptions, + customCssClasses, + delimiter, + facet, + collapsible = true, + defaultExpanded = true, + showMoreLimit = 4, +}: HierarchicalFacetProps & { facet: DisplayableFacet }) { + const options = facet.options || []; + const transformedOptions = transformOptions ? (transformOptions(options) || []) : options; + + return ( + + + + + + + ); +} diff --git a/src/components/HierarchicalFacets.tsx b/src/components/HierarchicalFacets.tsx index fe162869c..271817f2b 100644 --- a/src/components/HierarchicalFacets.tsx +++ b/src/components/HierarchicalFacets.tsx @@ -43,6 +43,7 @@ export interface HierarchicalFacetsProps extends Omit$; */ export function NumericalFacetContent({ fieldId, - customCssClasses, + label, transformOptions, + customCssClasses, getFilterDisplayName, facet, - label = '', showMoreLimit = 10, showOptionCounts = false, inputPrefix = DEFAULT_RANGE_INPUT_PREFIX, @@ -34,7 +34,7 @@ export function NumericalFacetContent({ filterOptions={transformedOptions.map(o => { return showOptionCounts ? { ...o, resultsCount: o.count } : o; })} - title={label} + title={label || facet.displayName} customCssClasses={customCssClasses} showMoreLimit={showMoreLimit} searchable={facet?.options.length > showMoreLimit} diff --git a/src/components/NumericalFacets.tsx b/src/components/NumericalFacets.tsx index 02d06e834..fe991e6af 100644 --- a/src/components/NumericalFacets.tsx +++ b/src/components/NumericalFacets.tsx @@ -49,6 +49,7 @@ const DEFAULT_RANGE_INPUT_PREFIX = <>$; * @param props - {@link NumericalFacetsProps} * @returns A React component for facets * + * @deprecated Use {@link Facets} instead. * @public */ export function NumericalFacets({ diff --git a/src/components/StandardFacetContent.tsx b/src/components/StandardFacetContent.tsx index a1155a7c7..243351ce6 100644 --- a/src/components/StandardFacetContent.tsx +++ b/src/components/StandardFacetContent.tsx @@ -12,10 +12,10 @@ import { StandardFacetProps } from './FacetProps'; */ export function StandardFacetContent({ fieldId, - customCssClasses, + label, transformOptions, + customCssClasses, facet, - label = '', showMoreLimit = 10, showOptionCounts = true, ...filterGroupProps @@ -29,7 +29,7 @@ export function StandardFacetContent({ filterOptions={transformedOptions.map(o => { return showOptionCounts ? { ...o, resultsCount: o.count } : o; })} - title={label} + title={label || facet.displayName} customCssClasses={customCssClasses} showMoreLimit={showMoreLimit} searchable={facet?.options.length > showMoreLimit} diff --git a/src/components/index.ts b/src/components/index.ts index 6216c3b54..fe2969523 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -128,7 +128,8 @@ export { export { Facets, StandardFacet, - NumericalFacet + NumericalFacet, + HierarchicalFacet } from './Facets'; export { @@ -136,7 +137,8 @@ export { FacetsProps, FacetProps, StandardFacetProps, - NumericalFacetProps + NumericalFacetProps, + HierarchicalFacetProps } from './FacetProps'; export { diff --git a/src/utils/filterutils.tsx b/src/utils/filterutils.tsx index 0f746e42c..fb81a90f4 100644 --- a/src/utils/filterutils.tsx +++ b/src/utils/filterutils.tsx @@ -2,6 +2,7 @@ import { NearFilterValue, FieldValueFilter, NumberRangeValue, Matcher, SearchAct import { isEqual } from 'lodash'; import { isNumberRangeFilter } from '../models/NumberRangeFilter'; import { SelectableFieldValueFilter } from '../models/SelectableFieldValueFilter'; +import { DEFAULT_HIERARCHICAL_DELIMITER } from '../components/Filters/HierarchicalFacetDisplay'; /** * Check if the object follows NearFilterValue interface. @@ -28,7 +29,8 @@ export function isStringFacet(facet: DisplayableFacet): boolean { * Checks if the facet is a numerical facet with number range filter options. */ export function isNumericalFacet(facet: DisplayableFacet): boolean { - return facet.options.length > 0 && isNumberRangeFilter(facet.options[0]); + return facet.options.length > 0 && + facet.options.some(option => isNumberRangeFilter(option)); } /** diff --git a/test-site/package-lock.json b/test-site/package-lock.json index 5a6e34d16..b5902bf7c 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,7 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", - "version": "1.2.0-beta.0", + "version": "1.2.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -80,7 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -97,7 +97,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -20112,7 +20112,7 @@ "@typescript-eslint/parser": "^5.16.0", "@yext/analytics": "^0.2.0-beta.3", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0-beta.0", + "@yext/search-headless-react": "^2.2.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "classnames": "^2.3.1", diff --git a/test-site/src/pages/PeoplePage.tsx b/test-site/src/pages/PeoplePage.tsx index 92d783314..91c6befdd 100644 --- a/test-site/src/pages/PeoplePage.tsx +++ b/test-site/src/pages/PeoplePage.tsx @@ -10,12 +10,16 @@ import { LocationBias, StaticFilters, StandardFacets, + Facets, + HierarchicalFacet, HierarchicalFacets, FilterDivider, ApplyFiltersButton, Pagination, NumericalFacets, - AlternativeVerticals + AlternativeVerticals, + StandardFacet, + NumericalFacet } from '@yext/search-ui-react'; // import { CustomCard } from '../components/CustomCard'; @@ -70,6 +74,15 @@ export function PeoplePage() { collapsible={true} includedFieldIds={hierarchicalFacetFieldIds} /> + + + + + + + + +
diff --git a/tests/__fixtures__/data/filters.ts b/tests/__fixtures__/data/filters.ts index 504144a7b..06165e324 100644 --- a/tests/__fixtures__/data/filters.ts +++ b/tests/__fixtures__/data/filters.ts @@ -1,6 +1,7 @@ import { DisplayableFacet, Matcher, SelectableStaticFilter } from '@yext/search-headless-react'; import { RemovableFilter } from '../../../src/components/AppliedFiltersDisplay'; import { StaticFiltersProps } from '../../../src/components/StaticFilters'; +import { createHierarchicalFacet } from '../../__utils__/hierarchicalfacets'; function createRemovableFilter(value: string) { return { @@ -72,7 +73,13 @@ export const DisplayableFacets: DisplayableFacet[] = [ } ], displayName: 'Price' - } + }, + createHierarchicalFacet([ + 'food', + 'food > fruit', + { value: 'food > fruit > banana', selected: true }, + 'food > fruit > apple', + ]) ]; export const staticFilters: SelectableStaticFilter[] = [ diff --git a/tests/__utils__/facets.ts b/tests/__utils__/facets.ts index 611fef181..9738ecf3e 100644 --- a/tests/__utils__/facets.ts +++ b/tests/__utils__/facets.ts @@ -1,5 +1,18 @@ -import { DisplayableFacetOption } from '@yext/search-headless-react'; +import { DisplayableFacetOption, FacetOption, SearchActions } from '@yext/search-headless-react'; -export function getOptionLabelText(option: DisplayableFacetOption) { +export function getOptionLabelTextWithCount(option: DisplayableFacetOption) { return `${option.displayName} (${option.count})`; } + +export function expectFacetOptionSet( + actions: SearchActions, + fieldId: string, + option: FacetOption, + selected: boolean +) { + expect(actions.setFacetOption).toHaveBeenCalledWith( + fieldId, + { matcher: option.matcher, value: option.value }, + selected + ); +} diff --git a/tests/components/Facets.test.tsx b/tests/components/Facets.test.tsx index c38c5bafc..7e48c2448 100644 --- a/tests/components/Facets.test.tsx +++ b/tests/components/Facets.test.tsx @@ -1,13 +1,18 @@ import { render, screen } from '@testing-library/react'; -import { - Source, - State -} from '@yext/search-headless-react'; -import { mockAnswersHooks, mockAnswersState } from '../__utils__/mocks'; +import { Source, State } from '@yext/search-headless-react'; +import { mockAnswersHooks, mockAnswersState, spyOnActions } from '../__utils__/mocks'; import { DisplayableFacets } from '../__fixtures__/data/filters'; -import { Facets, StandardFacet, StandardFacetProps, NumericalFacet, NumericalFacetProps } from '../../src'; -import { getOptionLabelText } from '../__utils__/facets'; +import { + Facets, + StandardFacet, + StandardFacetProps, + NumericalFacet, + NumericalFacetProps, + HierarchicalFacet, HierarchicalFacetProps +} from '../../src'; +import { expectFacetOptionSet, getOptionLabelTextWithCount } from '../__utils__/facets'; import { DisplayableFacetOption } from '@yext/search-core'; +import userEvent from '@testing-library/user-event'; const mockedState: Partial = { filters: { @@ -49,11 +54,11 @@ describe('Facets', () => { }); it('Properly renders standard facets if present', () => { render(); - const regularFilter = DisplayableFacets[0]; + const facet = DisplayableFacets[0]; - expect(screen.getByText(regularFilter.displayName)).toBeDefined(); - regularFilter.options.forEach(o => { - expect(screen.getByText(getOptionLabelText(o))).toBeDefined(); + expect(screen.getByText(facet.displayName)).toBeDefined(); + facet.options.forEach(o => { + expect(screen.getByText(getOptionLabelTextWithCount(o))).toBeDefined(); }); }); @@ -76,7 +81,7 @@ describe('Facets', () => { } }); render(); - const regularFilter = DisplayableFacets[0]; + const facet = DisplayableFacets[0]; const numericalFilter = DisplayableFacets[1]; expect(screen.queryByText(numericalFilter.displayName)).toBeNull(); @@ -84,8 +89,8 @@ describe('Facets', () => { expect(screen.queryByText(o.displayName)).toBeNull(); }); - expect(screen.queryByText(regularFilter.displayName)).toBeNull(); - regularFilter.options.forEach(o => { + expect(screen.queryByText(facet.displayName)).toBeNull(); + facet.options.forEach(o => { expect(screen.queryByText(`${o.displayName} (${o.count})}`)).toBeNull(); }); }); @@ -106,14 +111,14 @@ describe('Facets', () => { ); - const regularFilter = DisplayableFacets[0]; + const facet = DisplayableFacets[0]; expect(screen.getByText(overrideLabel)).toBeDefined(); - expect(screen.queryByText(regularFilter.displayName)).toBeNull(); + expect(screen.queryByText(facet.displayName)).toBeNull(); expect( screen .getByText( - `my ${regularFilter.options[0].displayName} (${regularFilter.options[0].count})`)) + `my ${facet.options[0].displayName} (${facet.options[0].count})`)) .toBeDefined(); }); @@ -133,10 +138,102 @@ describe('Facets', () => { ); - const regularFilter = DisplayableFacets[1]; + const facet = DisplayableFacets[1]; + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.queryByText(facet.displayName)).toBeNull(); + expect(screen.getByText(`Price is ${facet.options[0].displayName}`)).toBeDefined(); + }); + + it('Properly renders an override hierarchical facet if present', () => { + const overrideFieldId = 'hier'; + const overrideLabel = 'My Fruit'; + const props: HierarchicalFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + }; + + render( + + + ); + const facet = DisplayableFacets[2]; + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.queryByText(facet.displayName)).toBeNull(); + }); + + it('Clicking a facet option executes a search by default', () => { + mockAnswersState({ + ...mockedState, + filters: { facets: [DisplayableFacets[0]] } + }); + const actions = spyOnActions(); + render(); + + const facet = DisplayableFacets[0]; + const coffeeCheckbox: HTMLInputElement = screen.getByLabelText( + getOptionLabelTextWithCount(facet.options[0]) + ); + expect(coffeeCheckbox.checked).toBeFalsy(); + + userEvent.click(coffeeCheckbox); + expectFacetOptionSet(actions, facet.fieldId, facet.options[0], true); + expect(actions.executeVerticalQuery).toBeCalled(); + }); + + it('Clicking a facet option does not execute a search when searchOnChange is false', () => { + mockAnswersState({ + ...mockedState, + filters: { facets: [DisplayableFacets[0]] } + }); + const actions = spyOnActions(); + render(); + + const facet = DisplayableFacets[0]; + const coffeeCheckbox: HTMLInputElement = screen.getByLabelText( + getOptionLabelTextWithCount(facet.options[0]) + ); + expect(coffeeCheckbox.checked).toBeFalsy(); + + userEvent.click(coffeeCheckbox); + expectFacetOptionSet(actions, facet.fieldId, facet.options[0], true); + expect(actions.executeVerticalQuery).not.toBeCalled(); + }); + + it('Renders all facets by default', () => { + const overrideFieldId = 'products'; + const overrideLabel = 'My Products'; + const props: StandardFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + }; + + render( + + + ); + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.getByText(DisplayableFacets[1].displayName)).toBeDefined(); + expect(screen.getByText(DisplayableFacets[2].displayName)).toBeDefined(); + }); + + it('Only render customize facets if onlyRenderChildren is set to true', () => { + const overrideFieldId = 'products'; + const overrideLabel = 'My Products'; + const props: StandardFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + }; + + render( + + + ); expect(screen.getByText(overrideLabel)).toBeDefined(); - expect(screen.queryByText(regularFilter.displayName)).toBeNull(); - expect(screen.getByText(`Price is ${regularFilter.options[0].displayName}`)).toBeDefined(); + expect(screen.queryByText(DisplayableFacets[1].displayName)).toBeNull(); + expect(screen.queryByText(DisplayableFacets[2].displayName)).toBeNull(); }); -}); \ No newline at end of file +}); diff --git a/tests/components/HierarchicalFacet.stories.tsx b/tests/components/HierarchicalFacet.stories.tsx new file mode 100644 index 000000000..b2868fa05 --- /dev/null +++ b/tests/components/HierarchicalFacet.stories.tsx @@ -0,0 +1,37 @@ +import { ComponentMeta, Story } from '@storybook/react'; +import { Facets, HierarchicalFacetProps, HierarchicalFacet } from '../../src'; +import { SearchHeadlessContext, State } from '@yext/search-headless-react'; +import { generateMockedHeadless } from '../__fixtures__/search-headless'; +import { RecursivePartial } from '../__utils__/mocks'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; + +const meta: ComponentMeta = { + title: 'Facets', + component: Facets +}; +export default meta; + +const mockedHeadlessState: RecursivePartial = { + filters: { + facets: [ + ...DisplayableFacets, + createHierarchicalFacet([ + 'food', + 'food > fruit', + { value: 'food > fruit > banana', selected: true }, + 'food > fruit > apple', + ]) + ] + } +}; + +export const Primary: Story = (args) => { + return ( + + + + + + ); +}; \ No newline at end of file diff --git a/tests/components/HierarchicalFacetContent.test.tsx b/tests/components/HierarchicalFacetContent.test.tsx new file mode 100644 index 000000000..c76c136fd --- /dev/null +++ b/tests/components/HierarchicalFacetContent.test.tsx @@ -0,0 +1,141 @@ +import { render, screen } from '@testing-library/react'; +import { Matcher, Source, State } from '@yext/search-headless-react'; +import { spyOnActions, mockAnswersState, mockAnswersHooks } from '../__utils__/mocks'; +import userEvent from '@testing-library/user-event'; +import { HierarchicalFacetProps } from '../../src'; +import { HierarchicalFacetContent } from '../../src/components/HierarchicalFacetContent'; +import { FacetsProvider } from '../../src/components/Filters'; +import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; + +const hierarchicalFacet = DisplayableFacets[2]; + +const mockedState: Partial = { + filters: { + static: [], + facets: [hierarchicalFacet] + }, + vertical: { + verticalKey: 'vertical', + results: [{ + source: Source.KnowledgeManager, + rawData: {} + }], + appliedQueryFilters: [] + }, + searchStatus: { + isLoading: false + }, + meta: { + searchType: 'vertical' + } +}; + +const mockedActions = { + state: mockedState, + executeVerticalQuery: jest.fn(), + setOffset: jest.fn(), + setFilterOption: jest.fn(), + setFacetOption: jest.fn(), + resetFacets: jest.fn(), + setStaticFilters: jest.fn() +}; + +jest.mock('@yext/search-headless-react'); + +const mockHierarchicalFacet = (props?: HierarchicalFacetProps) => { + return ( + + {facets => facets.map(facet => ( + ))} + ); +}; + +describe('HierarchicalFacetsContent', () => { + beforeEach(() => { + mockAnswersHooks({ mockedState, mockedActions }); + }); + + it('Properly renders hierarchical facets', () => { + render(mockHierarchicalFacet()); + + expect(screen.getByRole('button', { name: /food/i })).toBeTruthy(); + expect(screen.getAllByRole('button', { name: /fruit/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /banana/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /apple/i })).toBeTruthy(); + }); + it('Clicking the currently selected option deselects it and selects its parent', () => { + const actions = spyOnActions(); + render(mockHierarchicalFacet()); + + const bananaButton = screen.getByRole('button', { name: /banana/i }); + userEvent.click(bananaButton); + + expectFacetOptionSet(actions, { value: 'food > fruit > banana', selected: false }); + expectFacetOptionSet(actions, { value: 'food > fruit', selected: true }); + }); + it('Clicking a non-selected option selects it and deselects its siblings', () => { + const actions = spyOnActions(); + render(mockHierarchicalFacet()); + + const appleButton = screen.getByRole('button', { name: /apple/i }); + userEvent.click(appleButton); + + expectFacetOptionSet(actions, { value: 'food > fruit > apple', selected: true }); + expectFacetOptionSet(actions, { value: 'food > fruit > banana', selected: false }); + }); + it('Clicking the current category with a selected child selects the category and deselects the ' + + 'child', () => { + const actions = spyOnActions(); + render(mockHierarchicalFacet()); + + const currentCategoryButton = screen.getAllByRole('button', { name: /fruit/i })[1]; + userEvent.click(currentCategoryButton); + + expectFacetOptionSet(actions, { value: 'food > fruit', selected: true }); + expectFacetOptionSet(actions, { value: 'food > fruit > banana', selected: false }); + }); + it('Clicking a selected current category deselects it and selects its parent', () => { + const facets = [ + createHierarchicalFacet([ + 'food', + { value: 'food > fruit', selected: true }, + 'food > fruit > banana', + 'food > fruit > apple', + ]) + ]; + + mockAnswersState({ + ...mockedState, + filters: { facets: facets } + }); + + const actions = spyOnActions(); + render(mockHierarchicalFacet()); + + const currentCategoryButton = screen.getAllByRole('button', { name: /fruit/i })[1]; + userEvent.click(currentCategoryButton); + + expectFacetOptionSet(actions, { value: 'food > fruit', selected: false }); + expectFacetOptionSet(actions, { value: 'food', selected: true }); + }); + it('Clicking a parent category selects it and deselects its children', () => { + const actions = spyOnActions(); + render(mockHierarchicalFacet()); + + const parentCategoryButton = screen.getByRole('button', { name: /food/i }); + userEvent.click(parentCategoryButton); + + expectFacetOptionSet(actions, { value: 'food', selected: true }); + expectFacetOptionSet(actions, { value: 'food > fruit', selected: false }); + expectFacetOptionSet(actions, { value: 'food > fruit > banana', selected: false }); + }); +}); + +function expectFacetOptionSet(actions, facet: { value: string, selected: boolean }) { + expect(actions.setFacetOption).toHaveBeenCalledWith( + 'hier', + { matcher: Matcher.Equals, value: facet.value }, + facet.selected + ); +} diff --git a/tests/components/NumericalFacetContent.test.tsx b/tests/components/NumericalFacetContent.test.tsx new file mode 100644 index 000000000..1b3c99ff3 --- /dev/null +++ b/tests/components/NumericalFacetContent.test.tsx @@ -0,0 +1,176 @@ +import { render, screen } from '@testing-library/react'; +import { SearchActions, FacetOption, Matcher, NumberRangeValue, SelectableStaticFilter, Source, State } from '@yext/search-headless-react'; +import { mockAnswersHooks, mockAnswersState, spyOnActions } from '../__utils__/mocks'; +import userEvent from '@testing-library/user-event'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { NumericalFacetProps } from '../../src'; +import { NumericalFacetContent } from '../../src/components/NumericalFacetContent'; +import { FacetsProvider } from '../../src/components/Filters'; +import { getOptionLabelTextWithCount } from '../__utils__/facets'; + +const numericalFacet = DisplayableFacets[1]; + +const mockedState: Partial = { + filters: { + static: [], + facets: [numericalFacet] + }, + vertical: { + verticalKey: 'vertical', + results: [{ + source: Source.KnowledgeManager, + rawData: {} + }], + appliedQueryFilters: [] + }, + searchStatus: { + isLoading: false + }, + meta: { + searchType: 'vertical' + } +}; + +const mockedActions = { + state: mockedState, + setOffset: jest.fn(), + setFacetOption: jest.fn(), + setFilterOption: jest.fn(), + executeVerticalQuery: jest.fn() +}; + +const mockedUtils = { + isCloseMatch: () => true +}; + +jest.mock('@yext/search-headless-react'); + +const mockNumericalFacet = (props?: NumericalFacetProps) => { + return ( + + {facets => facets.map(facet => ( + ))} + ); +}; + +describe('NumericalFacetContent', () => { + beforeEach(() => { + mockAnswersHooks({ mockedState, mockedActions, mockedUtils }); + }); + + it('Properly renders number range facets', () => { + render(mockNumericalFacet()); + + expect(screen.getByText(numericalFacet.displayName)).toBeDefined(); + numericalFacet.options.forEach(o => { + expect(screen.getByText(o.displayName)).toBeDefined(); + }); + }); + + it('Clicking a selected number range facet option checkbox unselects it', () => { + const actions = spyOnActions(); + render(mockNumericalFacet()); + + const expensiveCheckbox: HTMLInputElement = + screen.getByLabelText(numericalFacet.options[0].displayName); + expect(expensiveCheckbox.checked).toBeTruthy(); + + userEvent.click(expensiveCheckbox); + expectFacetOptionSet(actions, numericalFacet.fieldId, numericalFacet.options[0], false); + }); + + it('Clicking an unselected number range facet option checkbox selects it', () => { + const actions = spyOnActions(); + render(mockNumericalFacet()); + + const cheapCheckbox: HTMLInputElement = + screen.getByLabelText(numericalFacet.options[1].displayName); + expect(cheapCheckbox.checked).toBeFalsy(); + + userEvent.click(cheapCheckbox); + expectFacetOptionSet(actions, numericalFacet.fieldId, numericalFacet.options[1], true); + }); + + it('getFilterDisplayName field works as expected', () => { + const facets = [{ + ...numericalFacet, + options: numericalFacet.options.map(o => ({ ...o, selected: false })) + }]; + mockAnswersState({ + ...mockedState, + filters: { facets: facets } + }); + const getFilterDisplayName = (value: NumberRangeValue) => { + return 'start-' + value.start?.value + ' end-' + value.end?.value; + }; + const actions = spyOnActions(); + render(mockNumericalFacet( + { fieldId: numericalFacet.fieldId, getFilterDisplayName: getFilterDisplayName })); + + userEvent.type(screen.getByPlaceholderText('Min'), '1'); + userEvent.type(screen.getByPlaceholderText('Max'), '5'); + userEvent.click(screen.getByText('Apply')); + + const expectedSelectableFilter: SelectableStaticFilter = { + displayName: 'start-1 end-5', + selected: true, + filter: { + kind: 'fieldValue', + fieldId: 'price', + value: { + start: { + matcher: Matcher.GreaterThanOrEqualTo, + value: 1 + }, + end: { + matcher: Matcher.LessThanOrEqualTo, + value: 5 + } + }, + matcher: Matcher.Between + } + }; + expect(actions.setFilterOption).toHaveBeenCalledWith(expectedSelectableFilter); + }); + + it('inputPrefix field works as expected', () => { + render(mockNumericalFacet({ fieldId: numericalFacet.fieldId, inputPrefix: <>some prefix })); + + expect(screen.getAllByText('some prefix').length).toEqual(2); + }); + + it('Does not display option counts by default', () => { + render(mockNumericalFacet()); + + const label = screen.queryByLabelText(numericalFacet.options[0].displayName); + const labelAndCount = + screen.queryByLabelText(getOptionLabelTextWithCount(numericalFacet.options[0])); + + expect(label).toBeDefined(); + expect(labelAndCount).toBeNull(); + }); + + it('Display option counts if showOptionCounts is set to true', () => { + render(mockNumericalFacet({ fieldId: numericalFacet.fieldId, showOptionCounts: true })); + + const label = screen.queryByLabelText(numericalFacet.options[0].displayName); + const labelAndCount = + screen.queryByLabelText(getOptionLabelTextWithCount(numericalFacet.options[0])); + + expect(label).toBeNull(); + expect(labelAndCount).toBeDefined(); + }); +}); + +function expectFacetOptionSet( + actions: SearchActions, + fieldId: string, + option: FacetOption, + selected: boolean +) { + expect(actions.setFacetOption).toHaveBeenCalledWith( + fieldId, + { matcher: option.matcher, value: option.value }, + selected + ); +} diff --git a/tests/components/StandardFacet.stories.tsx b/tests/components/StandardFacet.stories.tsx index ef47eff76..b0f7b7125 100644 --- a/tests/components/StandardFacet.stories.tsx +++ b/tests/components/StandardFacet.stories.tsx @@ -14,15 +14,7 @@ export default meta; const mockedHeadlessState: RecursivePartial = { filters: { - facets: [ - ...DisplayableFacets, - createHierarchicalFacet([ - 'food', - 'food > fruit', - { value: 'food > fruit > banana', selected: true }, - 'food > fruit > apple', - ]) - ] + facets: DisplayableFacets } }; diff --git a/tests/components/StandardFacetContent.test.tsx b/tests/components/StandardFacetContent.test.tsx new file mode 100644 index 000000000..6a35a9bf5 --- /dev/null +++ b/tests/components/StandardFacetContent.test.tsx @@ -0,0 +1,120 @@ +import { render, screen } from '@testing-library/react'; +import { FacetOption, Source, State, SearchActions } from '@yext/search-headless-react'; +import { mockAnswersHooks, spyOnActions } from '../__utils__/mocks'; +import userEvent from '@testing-library/user-event'; +import { DisplayableFacets } from '../__fixtures__/data/filters'; +import { getOptionLabelTextWithCount } from '../__utils__/facets'; +import { StandardFacetContent } from '../../src/components/StandardFacetContent'; +import { StandardFacetProps } from '../../src/components'; +import { FacetsProvider } from '../../src/components/Filters'; + +const standardFacet = DisplayableFacets[0]; + +const mockedState: Partial = { + filters: { + static: [], + facets: [standardFacet] + }, + vertical: { + verticalKey: 'vertical', + results: [{ + source: Source.KnowledgeManager, + rawData: {} + }], + appliedQueryFilters: [] + }, + searchStatus: { + isLoading: false + }, + meta: { + searchType: 'vertical' + } +}; + +const mockedActions = { + state: mockedState, + setOffset: jest.fn(), + setFacetOption: jest.fn(), + executeVerticalQuery: jest.fn() +}; + +const mockedUtils = { + isCloseMatch: () => true +}; + +jest.mock('@yext/search-headless-react'); + +const mockStandardFacet = (props?: StandardFacetProps) => { + return ( + + {facets => facets.map(facet => ( + ))} + ); +}; + +describe('StandardFacetContent', () => { + beforeEach(() => { + mockAnswersHooks({ mockedState, mockedActions, mockedUtils }); + }); + + it('Display option counts by default', () => { + render(mockStandardFacet()); + + const coffeeLabel = screen.queryByLabelText(standardFacet.options[0].displayName); + const coffeeLabelAndCount = + screen.queryByLabelText(getOptionLabelTextWithCount(standardFacet.options[0])); + + expect(coffeeLabel).toBeNull(); + expect(coffeeLabelAndCount).toBeDefined(); + }); + + it('Does not display option counts if showOptionCounts is set to false', () => { + render(mockStandardFacet({ fieldId: standardFacet.fieldId, showOptionCounts: false })); + + const coffeeLabel = screen.queryByLabelText(standardFacet.options[0].displayName); + const coffeeLabelAndCount = + screen.queryByLabelText(getOptionLabelTextWithCount(standardFacet.options[0])); + + expect(coffeeLabel).toBeDefined(); + expect(coffeeLabelAndCount).toBeNull(); + }); + + it('Clicking an unselected facet option label selects it', () => { + const actions = spyOnActions(); + render(mockStandardFacet()); + + const coffeeFacetOption = standardFacet.options[0]; + const labelText = getOptionLabelTextWithCount(coffeeFacetOption); + const coffeeCheckbox: HTMLInputElement = screen.getByLabelText(labelText); + expect(coffeeCheckbox.checked).toBeFalsy(); + + const coffeeLabel = screen.getByText(labelText); + userEvent.click(coffeeLabel); + expectFacetOptionSet(actions, standardFacet.fieldId, coffeeFacetOption, true); + }); + + it('Clicking a selected facet option checkbox unselects it', () => { + const actions = spyOnActions(); + render(mockStandardFacet()); + + const teaCheckbox: HTMLInputElement = screen.getByLabelText( + getOptionLabelTextWithCount(standardFacet.options[1])); + expect(teaCheckbox.checked).toBeTruthy(); + + userEvent.click(teaCheckbox); + expectFacetOptionSet(actions, standardFacet.fieldId, standardFacet.options[1], false); + }); +}); + +function expectFacetOptionSet( + actions: SearchActions, + fieldId: string, + option: FacetOption, + selected: boolean +) { + expect(actions.setFacetOption).toHaveBeenCalledWith( + fieldId, + { matcher: option.matcher, value: option.value }, + selected + ); +} \ No newline at end of file diff --git a/tests/components/StandardFacets.stories.tsx b/tests/components/StandardFacets.stories.tsx index 976d0593c..832a4ee99 100644 --- a/tests/components/StandardFacets.stories.tsx +++ b/tests/components/StandardFacets.stories.tsx @@ -42,5 +42,5 @@ ShowMoreLimitClicked.args = { }; ShowMoreLimitClicked.play = ({ canvasElement }) => { const canvas = within(canvasElement); - userEvent.click(canvas.getByText('Show More')); + userEvent.click(canvas.queryAllByText('Show More')[0]); }; diff --git a/tests/components/StandardFacets.test.tsx b/tests/components/StandardFacets.test.tsx index 37c64206f..7917f03d9 100644 --- a/tests/components/StandardFacets.test.tsx +++ b/tests/components/StandardFacets.test.tsx @@ -1,10 +1,10 @@ import { render, screen } from '@testing-library/react'; -import { FacetOption, Source, State, SearchActions } from '@yext/search-headless-react'; +import { Source, State } from '@yext/search-headless-react'; import { mockAnswersHooks, spyOnActions } from '../__utils__/mocks'; import userEvent from '@testing-library/user-event'; import { DisplayableFacets } from '../__fixtures__/data/filters'; import { StandardFacets } from '../../src/components'; -import { getOptionLabelText } from '../__utils__/facets'; +import { expectFacetOptionSet, getOptionLabelTextWithCount } from '../__utils__/facets'; const mockedState: Partial = { filters: { @@ -53,7 +53,7 @@ describe('StandardFacets', () => { expect(screen.getByText(regularFilter.displayName)).toBeDefined(); regularFilter.options.forEach(o => { - expect(screen.getByText(getOptionLabelText(o))).toBeDefined(); + expect(screen.getByText(getOptionLabelTextWithCount(o))).toBeDefined(); }); expect(screen.queryByText(numericalFilter.displayName)).toBeNull(); @@ -68,7 +68,7 @@ describe('StandardFacets', () => { const productFacet = DisplayableFacets[0]; const coffeeCheckbox: HTMLInputElement = screen.getByLabelText( - getOptionLabelText(productFacet.options[0]) + getOptionLabelTextWithCount(productFacet.options[0]) ); expect(coffeeCheckbox.checked).toBeFalsy(); @@ -82,7 +82,7 @@ describe('StandardFacets', () => { const facets = DisplayableFacets[0]; const coffeeLabel = screen.queryByLabelText(facets.options[0].displayName); const coffeeLabelAndCount = screen.queryByLabelText( - getOptionLabelText(facets.options[0]) + getOptionLabelTextWithCount(facets.options[0]) ); expect(coffeeLabel).toBeDefined(); @@ -95,7 +95,7 @@ describe('StandardFacets', () => { const productFacet = DisplayableFacets[0]; const coffeeFacetOption = productFacet.options[0]; - const labelText = getOptionLabelText(coffeeFacetOption); + const labelText = getOptionLabelTextWithCount(coffeeFacetOption); const coffeeCheckbox: HTMLInputElement = screen.getByLabelText(labelText); expect(coffeeCheckbox.checked).toBeFalsy(); @@ -109,7 +109,8 @@ describe('StandardFacets', () => { render(); const productFacet = DisplayableFacets[0]; - const teaCheckbox: HTMLInputElement = screen.getByLabelText(getOptionLabelText(productFacet.options[1])); + const teaCheckbox: HTMLInputElement = screen.getByLabelText( + getOptionLabelTextWithCount(productFacet.options[1])); expect(teaCheckbox.checked).toBeTruthy(); userEvent.click(teaCheckbox); @@ -122,7 +123,7 @@ describe('StandardFacets', () => { const productFacet = DisplayableFacets[0]; const coffeeCheckbox: HTMLInputElement = screen.getByLabelText( - getOptionLabelText(productFacet.options[0]) + getOptionLabelTextWithCount(productFacet.options[0]) ); expect(coffeeCheckbox.checked).toBeFalsy(); @@ -137,7 +138,7 @@ describe('StandardFacets', () => { const productFacet = DisplayableFacets[0]; const coffeeCheckbox: HTMLInputElement = screen.getByLabelText( - getOptionLabelText(productFacet.options[0]) + getOptionLabelTextWithCount(productFacet.options[0]) ); expect(coffeeCheckbox.checked).toBeFalsy(); @@ -146,16 +147,3 @@ describe('StandardFacets', () => { expect(actions.executeVerticalQuery).not.toBeCalled(); }); }); - -function expectFacetOptionSet( - actions: SearchActions, - fieldId: string, - option: FacetOption, - selected: boolean -) { - expect(actions.setFacetOption).toHaveBeenCalledWith( - fieldId, - { matcher: option.matcher, value: option.value }, - selected - ); -} \ No newline at end of file From 0041b9d2ab2724310f922b1e3c09e3fb6043f5e3 Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Wed, 24 May 2023 15:00:11 -0400 Subject: [PATCH 16/23] Bump Version to 1.3.0-beta.0 (#374) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ed070921..2e2b53df1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.2.0", + "version": "1.3.0-beta.0", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", From 98f98e48e319d312fcb20eebafdf8cadf0e6c79a Mon Sep 17 00:00:00 2001 From: EmilyZhang777 <48967088+EmilyZhang777@users.noreply.github.com> Date: Tue, 30 May 2023 12:02:30 -0400 Subject: [PATCH 17/23] Add Facets Level Css Support for FilterGroup (#375) * Add Facets Level Css Support for FilterGroup This change allows users to provide css classes for FilterGroup on the Facets level. If same css classes are provided on the singular facet level, the latter will be used. J=BACK-2306 TEST=auto,manual Added a test for this. Also manually confirmed the behavior. --- docs/search-ui-react.facetscssclasses.md | 5 +- docs/search-ui-react.hierarchicalfacets.md | 2 +- ...search-ui-react.hierarchicalfacetsprops.md | 5 ++ docs/search-ui-react.md | 2 +- docs/search-ui-react.numericalfacets.md | 2 +- docs/search-ui-react.numericalfacetsprops.md | 5 ++ ...earch-ui-react.standardfacetscssclasses.md | 2 +- docs/search-ui-react.standardfacetsprops.md | 2 +- etc/search-ui-react.api.md | 6 +- package.json | 2 +- src/components/FacetProps.tsx | 6 +- src/components/Facets.tsx | 88 ++++++++++++------- src/components/HierarchicalFacets.tsx | 3 +- src/components/NumericalFacets.tsx | 3 +- src/components/StandardFacets.tsx | 4 +- src/utils/filterutils.tsx | 1 - tests/components/Facets.test.tsx | 42 +++++++++ tests/components/StandardFacet.stories.tsx | 1 - 18 files changed, 132 insertions(+), 49 deletions(-) diff --git a/docs/search-ui-react.facetscssclasses.md b/docs/search-ui-react.facetscssclasses.md index 2aebb472b..e78debde6 100644 --- a/docs/search-ui-react.facetscssclasses.md +++ b/docs/search-ui-react.facetscssclasses.md @@ -4,13 +4,14 @@ ## FacetsCssClasses interface -The CSS class interface for [Facets()](./search-ui-react.facets.md). +The CSS class interface for [Facets()](./search-ui-react.facets.md). Any [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) props will be overridden by the same props from customCssClasses on [StandardFacetProps](./search-ui-react.standardfacetprops.md), [NumericalFacetProps](./search-ui-react.numericalfacetprops.md), or [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md). Signature: ```typescript -export interface FacetsCssClasses +export interface FacetsCssClasses extends FilterGroupCssClasses ``` +Extends: [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) ## Properties diff --git a/docs/search-ui-react.hierarchicalfacets.md b/docs/search-ui-react.hierarchicalfacets.md index fff8a6796..5bb066c90 100644 --- a/docs/search-ui-react.hierarchicalfacets.md +++ b/docs/search-ui-react.hierarchicalfacets.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [Facets()](./search-ui-react.facets.md) instead. +> Use [HierarchicalFacet()](./search-ui-react.hierarchicalfacet.md) with [Facets()](./search-ui-react.facets.md) instead. > A component that displays hierarchical facets, in a tree level structure, applicable to the current vertical search. diff --git a/docs/search-ui-react.hierarchicalfacetsprops.md b/docs/search-ui-react.hierarchicalfacetsprops.md index 92d294366..6cc1e4caa 100644 --- a/docs/search-ui-react.hierarchicalfacetsprops.md +++ b/docs/search-ui-react.hierarchicalfacetsprops.md @@ -4,6 +4,11 @@ ## HierarchicalFacetsProps interface +> Warning: This API is now obsolete. +> +> Use [HierarchicalFacet()](./search-ui-react.hierarchicalfacet.md) with [Facets()](./search-ui-react.facets.md) instead. +> + Props for the [HierarchicalFacets()](./search-ui-react.hierarchicalfacets.md) component. Signature: diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 0bd986785..b4ec09bc3 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -64,7 +64,7 @@ | [CtaData](./search-ui-react.ctadata.md) | The shape of a StandardCard CTA field's data. | | [DirectAnswerCssClasses](./search-ui-react.directanswercssclasses.md) | The CSS class interface for [DirectAnswer()](./search-ui-react.directanswer.md). | | [DirectAnswerProps](./search-ui-react.directanswerprops.md) | Props for [DirectAnswer()](./search-ui-react.directanswer.md). | -| [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | The CSS class interface for [Facets()](./search-ui-react.facets.md). | +| [FacetsCssClasses](./search-ui-react.facetscssclasses.md) | The CSS class interface for [Facets()](./search-ui-react.facets.md). Any [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) props will be overridden by the same props from customCssClasses on [StandardFacetProps](./search-ui-react.standardfacetprops.md), [NumericalFacetProps](./search-ui-react.numericalfacetprops.md), or [HierarchicalFacetProps](./search-ui-react.hierarchicalfacetprops.md). | | [FacetsProps](./search-ui-react.facetsprops.md) | Props for the [Facets()](./search-ui-react.facets.md) component. | | [FilterGroupCssClasses](./search-ui-react.filtergroupcssclasses.md) | The CSS class interface for FilterGroup. | | [FilterGroupProps](./search-ui-react.filtergroupprops.md) | Props for the FilterGroup component. | diff --git a/docs/search-ui-react.numericalfacets.md b/docs/search-ui-react.numericalfacets.md index 290c485a0..c82440f03 100644 --- a/docs/search-ui-react.numericalfacets.md +++ b/docs/search-ui-react.numericalfacets.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [Facets()](./search-ui-react.facets.md) instead. +> Use [NumericalFacet()](./search-ui-react.numericalfacet.md) with [Facets()](./search-ui-react.facets.md) instead. > A component that displays numerical facets applicable to the current vertical search. diff --git a/docs/search-ui-react.numericalfacetsprops.md b/docs/search-ui-react.numericalfacetsprops.md index 49ff39a94..7c1a2cbe5 100644 --- a/docs/search-ui-react.numericalfacetsprops.md +++ b/docs/search-ui-react.numericalfacetsprops.md @@ -4,6 +4,11 @@ ## NumericalFacetsProps interface +> Warning: This API is now obsolete. +> +> Use [NumericalFacet()](./search-ui-react.numericalfacet.md) with [Facets()](./search-ui-react.facets.md) instead. +> + Props for the [NumericalFacets()](./search-ui-react.numericalfacets.md) component. Signature: diff --git a/docs/search-ui-react.standardfacetscssclasses.md b/docs/search-ui-react.standardfacetscssclasses.md index 06f87d59f..2152492e5 100644 --- a/docs/search-ui-react.standardfacetscssclasses.md +++ b/docs/search-ui-react.standardfacetscssclasses.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [Facets()](./search-ui-react.facets.md) instead. +> Use [StandardFacet()](./search-ui-react.standardfacet.md) with [Facets()](./search-ui-react.facets.md) instead. > The CSS class interface for [StandardFacets()](./search-ui-react.standardfacets.md). diff --git a/docs/search-ui-react.standardfacetsprops.md b/docs/search-ui-react.standardfacetsprops.md index 903e12f43..1aafac0f4 100644 --- a/docs/search-ui-react.standardfacetsprops.md +++ b/docs/search-ui-react.standardfacetsprops.md @@ -6,7 +6,7 @@ > Warning: This API is now obsolete. > -> Use [Facets()](./search-ui-react.facets.md) instead. +> Use [StandardFacet()](./search-ui-react.standardfacet.md) with [Facets()](./search-ui-react.facets.md) instead. > Props for the [StandardFacets()](./search-ui-react.standardfacets.md) component. diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index 47265dbce..5c4a2ea04 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -209,7 +209,7 @@ export type FacetProps = StandardFacetProps | NumericalFacetProps | Hierarchical export function Facets(props: FacetsProps): JSX.Element; // @public -export interface FacetsCssClasses { +export interface FacetsCssClasses extends FilterGroupCssClasses { // (undocumented) divider?: string; // (undocumented) @@ -378,7 +378,7 @@ export interface HierarchicalFacetsCssClasses extends HierarchicalFacetDisplayCs hierarchicalFacetsContainer?: string; } -// @public +// @public @deprecated export interface HierarchicalFacetsProps extends Omit { customCssClasses?: HierarchicalFacetsCssClasses; delimiter?: string; @@ -454,7 +454,7 @@ export interface NumericalFacetsCssClasses extends FilterGroupCssClasses, RangeI numericalFacetsContainer?: string; } -// @public +// @public @deprecated export interface NumericalFacetsProps extends Omit { customCssClasses?: NumericalFacetsCssClasses; getFilterDisplayName?: (value: NumberRangeValue) => string; diff --git a/package.json b/package.json index 2e2b53df1..804d8c9c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.0", + "version": "1.3.0-beta.1", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", diff --git a/src/components/FacetProps.tsx b/src/components/FacetProps.tsx index b713284ab..2966ec393 100644 --- a/src/components/FacetProps.tsx +++ b/src/components/FacetProps.tsx @@ -5,11 +5,13 @@ import { NumberRangeValue } from '@yext/search-headless-react'; import { HierarchicalFacetDisplayCssClasses, RangeInputCssClasses } from './Filters'; /** - * The CSS class interface for {@link Facets}. + * The CSS class interface for {@link Facets}. Any {@link FilterGroupCssClasses} props will be + * overridden by the same props from customCssClasses on {@link StandardFacetProps}, + * {@link NumericalFacetProps}, or {@link HierarchicalFacetProps}. * * @public */ -export interface FacetsCssClasses { +export interface FacetsCssClasses extends FilterGroupCssClasses { facetsContainer?: string, divider?: string } diff --git a/src/components/Facets.tsx b/src/components/Facets.tsx index eff844327..ee44e6030 100644 --- a/src/components/Facets.tsx +++ b/src/components/Facets.tsx @@ -79,39 +79,15 @@ export function Facets(props: FacetsProps) { && (!onlyRenderChildren || fieldIdToCustomFacetProps.has(fieldId))) .map((fieldId, i) => { const facet: DisplayableFacet = fieldIdToFacet.get(fieldId); - let facetType: FacetType = FacetType.STANDARD; - let facetProps: FacetProps = { - fieldId: facet.fieldId, - label: facet.displayName, - }; - if (fieldIdToCustomFacetProps.has(facet.fieldId)) { - const customFacetElement: ReactElement = - fieldIdToCustomFacetProps.get(facet.fieldId); - facetProps = { ...facetProps, ...customFacetElement.props }; - facetType = getFacetTypeFromReactElementType( - (typeof customFacetElement.type === 'function') - ? customFacetElement.type.name : ''); - } else { - facetType = getFacetTypeFromFacet(facet, hierarchicalFieldIds); - } - - let facetComponent: ReactElement; - switch (facetType) { - case FacetType.NUMERICAL: - facetComponent = (); - break; - case FacetType.HIERARCHICAL: - facetComponent = (); - break; - case FacetType.STANDARD: - // fall through - default: - facetComponent = (); - } return ( - {facetComponent} + {(i < facets.length - 1) && } @@ -155,6 +131,58 @@ export function NumericalFacet(props: NumericalFacetProps) { return null; } // eslint-disable-next-line @typescript-eslint/no-unused-vars export function HierarchicalFacet(props: HierarchicalFacetProps) { return null; } +/** + * A component that represents a single facet. + * + * @param facet - {@link DisplayableFacet} + * @param facetsCustomCssClasses - {@link FacetsCssClasses} + * @param fieldIdToCustomFacetProps - a map of fieldId to facet props + * @param hierarchicalFieldIds - a list of hierarchical field ids + * @returns {@link ReactElement} + * + * @internal + */ +export function Facet({ + facet, + facetsCustomCssClasses, + fieldIdToCustomFacetProps, + hierarchicalFieldIds, +}) { + let facetType: FacetType; + let facetProps: FacetProps = { + fieldId: facet.fieldId, + label: facet.displayName, + }; + if (fieldIdToCustomFacetProps.has(facet.fieldId)) { + const customFacetElement: ReactElement = fieldIdToCustomFacetProps.get(facet.fieldId); + facetProps = { ...facetProps, ...customFacetElement.props }; + facetType = getFacetTypeFromReactElementType( + (typeof customFacetElement.type === 'function') + ? customFacetElement.type.name : ''); + } else { + facetType = getFacetTypeFromFacet(facet, hierarchicalFieldIds); + } + + facetProps = { + ...facetProps, + customCssClasses: { + ...facetsCustomCssClasses, + ...facetProps.customCssClasses, + }, + }; + + switch (facetType) { + case FacetType.NUMERICAL: + return (); + case FacetType.HIERARCHICAL: + return (); + case FacetType.STANDARD: + // fall through + default: + return (); + } +} + /** * Returns the type of the facet based on the props. * @param elementType - string diff --git a/src/components/HierarchicalFacets.tsx b/src/components/HierarchicalFacets.tsx index 271817f2b..f9e3724e1 100644 --- a/src/components/HierarchicalFacets.tsx +++ b/src/components/HierarchicalFacets.tsx @@ -23,6 +23,7 @@ export interface HierarchicalFacetsCssClasses extends HierarchicalFacetDisplayCs /** * Props for the {@link HierarchicalFacets} component. * + * @deprecated Use {@link HierarchicalFacet} with {@link Facets} instead. * @public */ export interface HierarchicalFacetsProps extends Omit { @@ -43,7 +44,7 @@ export interface HierarchicalFacetsProps extends Omit { @@ -49,7 +50,7 @@ const DEFAULT_RANGE_INPUT_PREFIX = <>$; * @param props - {@link NumericalFacetsProps} * @returns A React component for facets * - * @deprecated Use {@link Facets} instead. + * @deprecated Use {@link NumericalFacet} with {@link Facets} instead. * @public */ export function NumericalFacets({ diff --git a/src/components/StandardFacets.tsx b/src/components/StandardFacets.tsx index 3b2ade707..a5bf6074c 100644 --- a/src/components/StandardFacets.tsx +++ b/src/components/StandardFacets.tsx @@ -7,7 +7,7 @@ import { isStringFacet } from '../utils/filterutils'; /** * The CSS class interface for {@link StandardFacets}. * - * @deprecated Use {@link Facets} instead. + * @deprecated Use {@link StandardFacet} with {@link Facets} instead. * @public */ export interface StandardFacetsCssClasses extends FilterGroupCssClasses { @@ -18,7 +18,7 @@ export interface StandardFacetsCssClasses extends FilterGroupCssClasses { /** * Props for the {@link StandardFacets} component. * - * @deprecated Use {@link Facets} instead. + * @deprecated Use {@link StandardFacet} with {@link Facets} instead. * @public */ export interface StandardFacetsProps { diff --git a/src/utils/filterutils.tsx b/src/utils/filterutils.tsx index fb81a90f4..3e0d9e7c5 100644 --- a/src/utils/filterutils.tsx +++ b/src/utils/filterutils.tsx @@ -2,7 +2,6 @@ import { NearFilterValue, FieldValueFilter, NumberRangeValue, Matcher, SearchAct import { isEqual } from 'lodash'; import { isNumberRangeFilter } from '../models/NumberRangeFilter'; import { SelectableFieldValueFilter } from '../models/SelectableFieldValueFilter'; -import { DEFAULT_HIERARCHICAL_DELIMITER } from '../components/Filters/HierarchicalFacetDisplay'; /** * Check if the object follows NearFilterValue interface. diff --git a/tests/components/Facets.test.tsx b/tests/components/Facets.test.tsx index 7e48c2448..65d0678aa 100644 --- a/tests/components/Facets.test.tsx +++ b/tests/components/Facets.test.tsx @@ -236,4 +236,46 @@ describe('Facets', () => { expect(screen.queryByText(DisplayableFacets[1].displayName)).toBeNull(); expect(screen.queryByText(DisplayableFacets[2].displayName)).toBeNull(); }); + + it('Use FilterGroupCssClasses provided on the Facets level if not provided on the singular facet', + () => { + const overrideFieldId = 'products'; + const overrideLabel = 'My Products'; + const facetsTitleLabelClass = 'facets-title-label-class'; + const props: StandardFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + }; + + render( + + + ); + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.getByText(overrideLabel)).toHaveClass(facetsTitleLabelClass); + expect(screen.getByText(DisplayableFacets[1].displayName)).toBeDefined(); + expect(screen.getByText(DisplayableFacets[1].displayName)).toHaveClass(facetsTitleLabelClass); + }); + + it('Use FilterGroupCssClasses provided on the singular facet level if provided', + () => { + const overrideFieldId = 'products'; + const overrideLabel = 'My Products'; + const facetsTitleLabelClass = 'facets-title-label-class'; + const standardFacetTitleLabelClass = 'standard-facet-title-label-class'; + const props: StandardFacetProps = { + fieldId: overrideFieldId, + label: overrideLabel, + customCssClasses: { titleLabel: standardFacetTitleLabelClass }, + }; + + render( + + + ); + + expect(screen.getByText(overrideLabel)).toBeDefined(); + expect(screen.getByText(overrideLabel)).toHaveClass(standardFacetTitleLabelClass); + }); }); diff --git a/tests/components/StandardFacet.stories.tsx b/tests/components/StandardFacet.stories.tsx index b0f7b7125..834ffd206 100644 --- a/tests/components/StandardFacet.stories.tsx +++ b/tests/components/StandardFacet.stories.tsx @@ -4,7 +4,6 @@ import { SearchHeadlessContext, State } from '@yext/search-headless-react'; import { generateMockedHeadless } from '../__fixtures__/search-headless'; import { RecursivePartial } from '../__utils__/mocks'; import { DisplayableFacets } from '../__fixtures__/data/filters'; -import { createHierarchicalFacet } from '../__utils__/hierarchicalfacets'; const meta: ComponentMeta = { title: 'Facets', From 84306bff93e40f6a776778b7f83dd30f38f5864d Mon Sep 17 00:00:00 2001 From: Saahith Janapati <22772542+saahithjanapati@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:28:00 -0400 Subject: [PATCH 18/23] Remove logo searchbar (#376) removed logo from search bar and increased height of search field J=BACK-2305 T=manual Ran test site locally and also checked with UX team --- src/components/SearchBar.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 12384a43c..a922d4382 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -18,7 +18,6 @@ import { VerticalDividerIcon } from '../icons/VerticalDividerIcon'; import { HistoryIcon as RecentSearchIcon } from '../icons/HistoryIcon'; import { CloseIcon } from '../icons/CloseIcon'; import { MagnifyingGlassIcon } from '../icons/MagnifyingGlassIcon'; -import { YextIcon } from '../icons/YextIcon'; import { Dropdown } from './Dropdown/Dropdown'; import { useDropdownContext } from './Dropdown/DropdownContext'; import { DropdownInput } from './Dropdown/DropdownInput'; @@ -42,7 +41,7 @@ import { recursivelyMapChildren } from './utils/recursivelyMapChildren'; const builtInCssClasses: Readonly = { searchBarContainer: 'h-12 mb-6', inputDivider: 'border-t border-gray-200 mx-2.5', - inputElement: 'outline-none flex-grow border-none h-full pl-0.5 pr-2 text-neutral-dark text-base placeholder:text-neutral-light', + inputElement: 'outline-none flex-grow border-none h-11 pl-5 pr-2 text-neutral-dark text-base placeholder:text-neutral-light', searchButtonContainer: ' w-8 h-full mx-2 flex flex-col justify-center items-center', searchButton: 'h-7 w-7', focusedOption: 'bg-gray-100', @@ -406,9 +405,6 @@ export function SearchBar({ onToggle={handleToggleDropdown} >
-
- -
{renderInput()} {query && renderClearButton()} Date: Tue, 13 Jun 2023 11:10:23 -0400 Subject: [PATCH 19/23] truncate search autcomplete results and prevent resizing (#378) Made long autocomplete results show up on one line, truncated by ellipses. Also stopped search icon and history icon from getting resized along with the search bar Before: https://drive.google.com/file/d/1L-D8lFfdWw53uBAQ4pU2fxuvtAlOOahj/view?usp=sharing After: https://drive.google.com/file/d/15Jd5jZ0yXMoDTWMrOT1VsqspbmV0bePc/view?usp=sharing J=BACK-2239 TEST=manual ran on test site and verified with product team --- src/components/SearchBar.tsx | 4 ++-- src/components/utils/renderAutocompleteResult.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index a922d4382..2675b943c 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -47,8 +47,8 @@ const builtInCssClasses: Readonly = { focusedOption: 'bg-gray-100', clearButton: 'h-3 w-3 mr-3.5', verticalDivider: 'mr-0.5', - recentSearchesIcon: 'w-5 mr-1 text-gray-400', - recentSearchesOption: 'pl-3 text-neutral-dark', + recentSearchesIcon: 'w-5 mr-1 flex-shrink-0 h-full text-gray-400', + recentSearchesOption: 'whitespace-no-wrap max-w-full px-3 text-neutral-dark truncate', recentSearchesNonHighlighted: 'font-normal', // Swap this to semibold once we apply highlighting to recent searches verticalLink: 'ml-12 pl-1 text-neutral italic', entityPreviewsDivider: 'h-px bg-gray-200 mt-1 mb-4 mx-3.5', diff --git a/src/components/utils/renderAutocompleteResult.tsx b/src/components/utils/renderAutocompleteResult.tsx index da1d0098e..9f762bd00 100644 --- a/src/components/utils/renderAutocompleteResult.tsx +++ b/src/components/utils/renderAutocompleteResult.tsx @@ -14,8 +14,8 @@ export interface AutocompleteResultCssClasses { } export const builtInCssClasses: Readonly = { - option: 'flex whitespace-pre-wrap h-6.5 pl-3 text-neutral-dark', - icon: 'w-6 text-gray-400' + option: 'whitespace-no-wrap max-w-full px-3 text-neutral-dark truncate', + icon: 'w-6 h-full flex-shrink-0 text-gray-400' }; /** From beb792aae13e17139a93740e60153de38227d9c1 Mon Sep 17 00:00:00 2001 From: Saahith Janapati <22772542+saahithjanapati@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:19:02 -0400 Subject: [PATCH 20/23] add support to display richtextv2 and markdown that has been converted to HTML (#379) * add support for rendering RichTextv2 and markdown converted to HTML J=BACK-2241 TEST=manual ran test-site of search-ui-react with entity that had formatted RichTextv2 data and verified that it was rendered properly --------- --- THIRD-PARTY-NOTICES | 6 +-- package-lock.json | 52 +++++++++---------- package.json | 6 +-- .../FeaturedSnippetDirectAnswer.tsx | 24 +++++++-- test-site/package-lock.json | 8 +-- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index 17221cb2e..dcdb417f9 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -1020,7 +1020,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-core@2.3.0 + - @yext/search-core@2.4.0-beta.0 This package contains the following license and notice below: @@ -1064,7 +1064,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless-react@2.2.0 + - @yext/search-headless-react@2.3.0-beta.0 This package contains the following license and notice below: @@ -1108,7 +1108,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless@2.3.0 + - @yext/search-headless@2.4.0-beta.0 This package contains the following license and notice below: diff --git a/package-lock.json b/package-lock.json index 2d05180a8..c5a6a6d86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-ui-react", - "version": "1.2.0", + "version": "1.3.0-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yext/search-ui-react", - "version": "1.2.0", + "version": "1.3.0-beta.2", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -54,7 +54,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -71,7 +71,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -10738,9 +10738,9 @@ } }, "node_modules/@yext/search-core": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0.tgz", - "integrity": "sha512-vSvNXWv9E/6s4oRB1og4zHfRTTEHrmUm2sh95Y1Dn94U2mkjNDGSsshEeamU2UIJO7Ee5oT6K6JDU7XAVOxC4A==", + "version": "2.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0-beta.0.tgz", + "integrity": "sha512-daes8WoldwfPmCVxiiT/WZMaBBF7S3ax3pzVLc+z3nmnRDDQBLY1TrnYQqsasfb1YKpy4Qv/+6y/d2pR4IdYgA==", "dev": true, "dependencies": { "@babel/runtime-corejs3": "^7.12.5", @@ -10751,24 +10751,24 @@ } }, "node_modules/@yext/search-headless": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.3.0.tgz", - "integrity": "sha512-Uh5DVeV99dkaeF6ayuUEkcUbI8wHn/7bz4aHrjtdDyl3F/6GX3cyHbs/BQh1kWCr+t8EJUWVPl5s4jmL3tst/Q==", + "version": "2.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0-beta.0.tgz", + "integrity": "sha512-m5+2chjp3hm80pMpNLMBSqECW4B0WmQg+gQdLVvYZRm9EjQeyR2G9gtU6IyjwcmOL+JY2sMoG5Z2yxUPJtHCPw==", "dev": true, "dependencies": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.3.0", + "@yext/search-core": "2.4.0-beta.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "node_modules/@yext/search-headless-react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0.tgz", - "integrity": "sha512-x2Sx7uS3w5E8RfuIpPsQZGWTGNKS9kRWDlQReUngzxP+UhoBQ8L7qNTGGbJLZ+LUpjRqTS1RuZP4scljbla//g==", + "version": "2.3.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0-beta.0.tgz", + "integrity": "sha512-hFcWOvcyEYSvBO5YHCFqory/j/Pm/geDg0ENH29J9i3uMa1i5sTCWpUhh5HjOpJBPt3rLmmLKa1KTMQf5t9sTA==", "dev": true, "dependencies": { - "@yext/search-headless": "^2.3.0", + "@yext/search-headless": "2.4.0-beta.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { @@ -41108,9 +41108,9 @@ } }, "@yext/search-core": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.3.0.tgz", - "integrity": "sha512-vSvNXWv9E/6s4oRB1og4zHfRTTEHrmUm2sh95Y1Dn94U2mkjNDGSsshEeamU2UIJO7Ee5oT6K6JDU7XAVOxC4A==", + "version": "2.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0-beta.0.tgz", + "integrity": "sha512-daes8WoldwfPmCVxiiT/WZMaBBF7S3ax3pzVLc+z3nmnRDDQBLY1TrnYQqsasfb1YKpy4Qv/+6y/d2pR4IdYgA==", "dev": true, "requires": { "@babel/runtime-corejs3": "^7.12.5", @@ -41118,24 +41118,24 @@ } }, "@yext/search-headless": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.3.0.tgz", - "integrity": "sha512-Uh5DVeV99dkaeF6ayuUEkcUbI8wHn/7bz4aHrjtdDyl3F/6GX3cyHbs/BQh1kWCr+t8EJUWVPl5s4jmL3tst/Q==", + "version": "2.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0-beta.0.tgz", + "integrity": "sha512-m5+2chjp3hm80pMpNLMBSqECW4B0WmQg+gQdLVvYZRm9EjQeyR2G9gtU6IyjwcmOL+JY2sMoG5Z2yxUPJtHCPw==", "dev": true, "requires": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "^2.3.0", + "@yext/search-core": "2.4.0-beta.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "@yext/search-headless-react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.2.0.tgz", - "integrity": "sha512-x2Sx7uS3w5E8RfuIpPsQZGWTGNKS9kRWDlQReUngzxP+UhoBQ8L7qNTGGbJLZ+LUpjRqTS1RuZP4scljbla//g==", + "version": "2.3.0-beta.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0-beta.0.tgz", + "integrity": "sha512-hFcWOvcyEYSvBO5YHCFqory/j/Pm/geDg0ENH29J9i3uMa1i5sTCWpUhh5HjOpJBPt3rLmmLKa1KTMQf5t9sTA==", "dev": true, "requires": { - "@yext/search-headless": "^2.3.0", + "@yext/search-headless": "2.4.0-beta.0", "use-sync-external-store": "^1.1.0" } }, diff --git a/package.json b/package.json index 804d8c9c4..acd9f994a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.1", + "version": "1.3.0-beta.2", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", @@ -76,7 +76,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -93,7 +93,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" }, diff --git a/src/components/FeaturedSnippetDirectAnswer.tsx b/src/components/FeaturedSnippetDirectAnswer.tsx index 97edc630f..f5c15b870 100644 --- a/src/components/FeaturedSnippetDirectAnswer.tsx +++ b/src/components/FeaturedSnippetDirectAnswer.tsx @@ -1,5 +1,6 @@ import { FeaturedSnippetDirectAnswer as FeaturedSnippetDirectAnswerType } from '@yext/search-headless-react'; import { renderHighlightedValue } from './utils/renderHighlightedValue'; +import { useMemo } from 'react'; /** * Props for {@link FeaturedSnippetDirectAnswer}. @@ -29,17 +30,32 @@ interface FeaturedSnippetDirectAnswerCssClasses { * * @internal */ + +const unsupportedTextFormats: string[] = ['rich_text', 'rich_text_v2', 'markdown']; + export function FeaturedSnippetDirectAnswer({ result, readMoreClickHandler, cssClasses = {} }: FeaturedSnippetDirectAnswerProps): JSX.Element { const answer = result.fieldType === 'multi_line_text' && result.value; - // TODO: SLAP-2340, update rich text snippets to convert the markdown - if (result.fieldType === 'rich_text') { - console.warn('Rendering markdown for rich text direct answer is currently not supported. Displaying the unrendered markdown string as is.'); + if (unsupportedTextFormats.includes(result.fieldType)) { + console.warn('Rendering ' + result.fieldType + ' direct answer is currently not supported. ' + + 'You can modify your search configuration to convert ' + result.fieldType + ' to HTML to be rendered ' + + 'on the page.'); + } + let snippet: JSX.Element; + const snippetValue = useMemo(() => + { return { __html: result.snippet?.value }; }, [result.snippet?.value]); + + if (result.fieldType === 'html') { + snippet = ( +
+ ); + } + else { + snippet = renderHighlightedValue(result.snippet, { highlighted: cssClasses.highlighted }); } - const snippet = renderHighlightedValue(result.snippet, { highlighted: cssClasses.highlighted }); const link = result.relatedResult.link || result.relatedResult.rawData.landingPageUrl as string; const name = result.relatedResult.name; const snippetLinkMessage = 'Read more about '; diff --git a/test-site/package-lock.json b/test-site/package-lock.json index b5902bf7c..54fea188a 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,7 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", - "version": "1.2.0", + "version": "1.3.0-beta.2", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -80,7 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -97,7 +97,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "react": "^16.14 || ^17 || ^18", "react-dom": "^16.14 || ^17 || ^18" } @@ -20112,7 +20112,7 @@ "@typescript-eslint/parser": "^5.16.0", "@yext/analytics": "^0.2.0-beta.3", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "^2.2.0", + "@yext/search-headless-react": "2.3.0-beta.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "classnames": "^2.3.1", From b52c016ca938144360819000825516a978e5da63 Mon Sep 17 00:00:00 2001 From: Connor Anderson Date: Wed, 28 Jun 2023 21:16:51 -0400 Subject: [PATCH 21/23] notices file --- THIRD-PARTY-NOTICES | 5144 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5144 insertions(+) create mode 100644 THIRD-PARTY-NOTICES diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES new file mode 100644 index 000000000..dcdb417f9 --- /dev/null +++ b/THIRD-PARTY-NOTICES @@ -0,0 +1,5144 @@ +The following NPM packages may be included in this product: + + - @babel/runtime-corejs3@7.17.2 + - @babel/runtime@7.18.9 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/geojson-rewind@0.5.2 + +This package contains the following license and notice below: + +Copyright (c) 2020, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/geojson-types@1.0.2 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2018 Mapbox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/jsonlint-lines-primitives@2.0.2 + +This package contains the following license and notice below: + +JSON Lint +========= + +A fork of the `lines-primitive` branch of [tmcw/jsonlint](https://github.com/tmcw/jsonlint), which is a fork of [zaach/jsonlint](https://github.com/zaach/jsonlint) that adds line number annotations to the parsed JSON. + +This fork is used by Mapbox GL JS, specifically for providing helpful error messages when validating Mapbox GL style JSON documents. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/mapbox-gl-supported@2.0.1 + +This package contains the following license and notice below: + +BSD 3-Clause License + +Copyright (c) 2017, Mapbox +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/point-geometry@0.1.0 + +This package contains the following license and notice below: + +Copyright (c) 2015, Mapbox <> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/tiny-sdf@2.0.5 + +This package contains the following license and notice below: + +Copyright © 2016-2021 Mapbox, Inc. +This code available under the terms of the BSD 2-Clause license. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/unitbezier@0.0.0 + +This package contains the following license and notice below: + +[![Build Status](https://travis-ci.org/mapbox/unitbezier.svg)](https://travis-ci.org/mapbox/unitbezier) + +# unitbezier + +Unit bezier interpolation function: a port to JavaScript from Webkit: + +http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h + +## api + +### new UnitBezier(p1x, p1y, p2x, p2y) + +Initialize a new bezier curve given the points + +### bezier.sampleCurveX(t) + +### bezier.sampleCurveY(t) + +### bezier.sampleCurveDerivativeX(t) + +### bezier.solveCurveX(t) + +### bezier.solve(x, epsilon) + +----------- + +The following NPM package may be included in this product: + + - @mapbox/vector-tile@1.3.1 + +This package contains the following license and notice below: + +Copyright (c) 2014, Mapbox + + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Mapbox nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @mapbox/whoots-js@3.1.0 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @microsoft/api-documenter@7.15.3 + +This package contains the following license and notice below: + +@microsoft/api-documenter + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - @microsoft/api-extractor-model@7.15.3 + - @microsoft/api-extractor@7.19.4 + +These packages each contain the following license and notice below: + +@microsoft/api-extractor + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - @microsoft/tsdoc-config@0.15.2 + - @microsoft/tsdoc@0.13.2 + +These packages each contain the following license and notice below: + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - @nodelib/fs.scandir@2.1.5 + - @nodelib/fs.stat@2.0.5 + - @nodelib/fs.walk@1.2.8 + - fast-glob@3.2.11 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) Denis Malinochkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @popperjs/core@2.11.2 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2019 Federico Zivolo + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - @reach/auto-id@0.18.0 + - @reach/utils@0.18.0 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2018-2022, React Training LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @react-aria/ssr@3.3.0 + +This package contains the following license and notice below: + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Adobe + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +----------- + +The following NPM package may be included in this product: + + - @reduxjs/toolkit@1.8.6 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2018 Mark Erikson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @restart/hooks@0.4.5 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2018 Jason Quense + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @restart/ui@1.0.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 react-bootstrap + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @rushstack/node-core-library@3.45.0 + +This package contains the following license and notice below: + +@rushstack/node-core-library + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @rushstack/rig-package@0.3.7 + +This package contains the following license and notice below: + +@rushstack/rig-package + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @rushstack/ts-command-line@4.10.6 + +This package contains the following license and notice below: + +@rushstack/ts-command-line + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - @tailwindcss/forms@0.5.0 + - tailwindcss@3.1.8 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) Tailwind Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - @types/argparse@1.0.38 + +This package contains the following license and notice below: + +MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +----------- + +The following NPM packages may be included in this product: + + - @types/node@12.20.24 + - @types/prop-types@15.7.4 + - @types/react@17.0.38 + - @types/scheduler@0.16.2 + +These packages each contain the following license and notice below: + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE + +----------- + +The following NPM package may be included in this product: + + - @types/warning@3.0.0 + +This package contains the following license and notice below: + +# Installation +> `npm install --save @types/warning` + +# Summary +This package contains type definitions for warning (https://github.com/BerkeleyTrue/warning). + +# Details +Files were exported from https://www.github.com/DefinitelyTyped/DefinitelyTyped/tree/types-2.0/warning + +Additional Details + * Last updated: Thu, 01 Dec 2016 00:24:42 GMT + * File structure: ProperModule + * Library Dependencies: none + * Module Dependencies: none + * Global values: warning + +# Credits +These definitions were written by Chi Vinh Le . + +----------- + +The following NPM package may be included in this product: + + - @yext/analytics@0.2.0-beta.3 + +This package contains the following license and notice below: + +The Analytics files listed in this repository are licensed under the below license. All other features and products are subject to separate agreements +and certain functionality requires paid subscriptions to Yext products. + +BSD 3-Clause License + +Copyright (c) 2022, Yext +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @yext/search-core@2.4.0-beta.0 + +This package contains the following license and notice below: + +The Search Core files listed in this repository are licensed under the below license.  All other features and products are subject to separate agreements +and certain functionality requires paid subscriptions to Yext products. + +Contains information from the language-subtag-registry JSON Database (https://github.com/mattcg/language-subtag-registry/tree/master/data/json) +which is made available under the ODC Attribution License (https://github.com/mattcg/language-subtag-registry/blob/master/LICENSE.md). + +BSD 3-Clause License + +Copyright (c) 2022, Yext +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @yext/search-headless-react@2.3.0-beta.0 + +This package contains the following license and notice below: + +Contains information from the language-subtag-registry JSON Database (https://github.com/mattcg/language-subtag-registry/tree/master/data/json) +which is made available under the ODC Attribution License (https://github.com/mattcg/language-subtag-registry/blob/master/LICENSE.md). + +The Search Headless React files listed in this repository are licensed under the below license. All other features and products are subject to +separate agreements and certain functionality requires paid subscriptions to Yext products. + +BSD 3-Clause License + +Copyright (c) 2022, Yext +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - @yext/search-headless@2.4.0-beta.0 + +This package contains the following license and notice below: + +The Search Headless files listed in this repository are licensed under the below license.  All other features and products are subject to separate agreements +and certain functionality requires paid subscriptions to Yext products. + +Contains information from the language-subtag-registry JSON Database (https://github.com/mattcg/language-subtag-registry/tree/master/data/json) +which is made available under the ODC Attribution License (https://github.com/mattcg/language-subtag-registry/blob/master/LICENSE.md). + +BSD 3-Clause License + +Copyright (c) 2022, Yext +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - acorn-node@1.8.2 + +This package contains the following license and notice below: + +# [Apache License 2.0](https://spdx.org/licenses/Apache-2.0) + +Copyright 2018 Renée Kooi + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +> http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +## acorn-bigint + +The code in the `lib/bigint` folder is compiled from code licensed as MIT: + +> Copyright (C) 2017-2018 by Adrian Heine +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +Find the source code at https://github.com/acornjs/acorn-bigint. + +## acorn-import-meta + +The code in the `lib/import-meta` folder is compiled from code licensed as MIT: + +> Copyright (C) 2017-2018 by Adrian Heine +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. + +Find the source code at https://github.com/acornjs/acorn-import-meta. + +## acorn-dynamic-import + +The code in the `lib/dynamic-import` folder is licensed as MIT: + +> MIT License +> +> Copyright (c) 2016 Jordan Gensler +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +Find the source code at https://github.com/kesne/acorn-dynamic-import. + +----------- + +The following NPM package may be included in this product: + + - acorn-walk@7.2.0 + +This package contains the following license and notice below: + +Copyright (C) 2012-2018 by various contributors (see AUTHORS) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - acorn@7.4.1 + +This package contains the following license and notice below: + +MIT License + +Copyright (C) 2012-2018 by various contributors (see AUTHORS) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - ajv@6.12.6 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015-2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - anymatch@3.1.2 + +This package contains the following license and notice below: + +The ISC License + +Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com) + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - arg@5.0.2 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2021 Vercel, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - argparse@1.0.10 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (C) 2012 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - binary-extensions@2.2.0 + - is-binary-path@2.1.0 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) 2019 Sindre Sorhus (https://sindresorhus.com), Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - braces@3.0.2 + - normalize-path@3.0.0 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-2018, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - camelcase-css@2.0.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) Steven Vachon (svachon.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - chokidar@3.5.3 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - classnames@2.3.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2018 Jed Watson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - color-name@1.1.4 + +This package contains the following license and notice below: + +The MIT License (MIT) +Copyright (c) 2015 Dmitry Ivanov + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - colors@1.2.5 + +This package contains the following license and notice below: + +MIT License + +Original Library + - Copyright (c) Marak Squires + +Additional Functionality + - Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - commander@2.20.3 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - core-js-pure@3.21.1 + +This package contains the following license and notice below: + +Copyright (c) 2014-2022 Denis Pushkarev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - cross-fetch@3.1.5 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2017 Leonardo Quixadá + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - csscolorparser@1.0.3 + +This package contains the following license and notice below: + +https://github.com/deanm/css-color-parser-js + +JavaScript parser for CSS color strings. + +> parseCSSColor(' rgba (255, 128, 12, 0.5)'); + [ 255, 128, 12, 0.5 ] +> parseCSSColor('#fff'); + [ 255, 255, 255, 1 ] +> parseCSSColor('#ff0011'); + [ 255, 0, 17, 1 ] +> parseCSSColor('slateblue'); + [ 106, 90, 205, 1 ] +> parseCSSColor('blah'); + null +> parseCSSColor('ffffff'); + null +> parseCSSColor('hsla(900, 15%, 90%, 0.5)') + [ 226, 233, 233, 0.5 ] +> parseCSSColor('hsla(900, 15%, 90%)') + null +> parseCSSColor('hsl(900, 15%, 90%)') + [ 226, 233, 233, 1 ] +> parseCSSColor('hsl(900, 0.15, 90%)') // NOTE: not spec compliant. + [ 226, 233, 233, 1 ] + + +(c) Dean McNamee , 2012. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - cssesc@3.0.0 + - punycode@2.1.1 + +These packages each contain the following license and notice below: + +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - csstype@3.0.10 + +This package contains the following license and notice below: + +Copyright (c) 2017-2018 Fredrik Nicol + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - defined@1.0.0 + - detective@5.2.1 + - minimist@1.2.6 + +These packages each contain the following license and notice below: + +This software is released under the MIT license: + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - dequal@2.0.2 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) Luke Edwards (lukeed.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - didyoumean@1.2.2 + +This package contains the following license and notice below: + +## License + +didYouMean.js copyright (c) 2013 Dave Porter. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License +[here](http://www.apache.org/licenses/LICENSE-2.0). + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +----------- + +The following NPM package may be included in this product: + + - dlv@1.1.3 + +This package contains the following license and notice below: + +# `dlv(obj, keypath)` [![NPM](https://img.shields.io/npm/v/dlv.svg)](https://npmjs.com/package/dlv) [![Build](https://travis-ci.org/developit/dlv.svg?branch=master)](https://travis-ci.org/developit/dlv) + +> Safely get a dot-notated path within a nested object, with ability to return a default if the full key path does not exist or the value is undefined + + +### Why? + +Smallest possible implementation: only **130 bytes.** + +You could write this yourself, but then you'd have to write [tests]. + +Supports ES Modules, CommonJS and globals. + + +### Installation + +`npm install --save dlv` + + +### Usage + +`delve(object, keypath, [default])` + +```js +import delve from 'dlv'; + +let obj = { + a: { + b: { + c: 1, + d: undefined, + e: null + } + } +}; + +//use string dot notation for keys +delve(obj, 'a.b.c') === 1; + +//or use an array key +delve(obj, ['a', 'b', 'c']) === 1; + +delve(obj, 'a.b') === obj.a.b; + +//returns undefined if the full key path does not exist and no default is specified +delve(obj, 'a.b.f') === undefined; + +//optional third parameter for default if the full key in path is missing +delve(obj, 'a.b.f', 'foo') === 'foo'; + +//or if the key exists but the value is undefined +delve(obj, 'a.b.d', 'foo') === 'foo'; + +//Non-truthy defined values are still returned if they exist at the full keypath +delve(obj, 'a.b.e', 'foo') === null; + +//undefined obj or key returns undefined, unless a default is supplied +delve(undefined, 'a.b.c') === undefined; +delve(undefined, 'a.b.c', 'foo') === 'foo'; +delve(obj, undefined, 'foo') === 'foo'; +``` + + +### Setter Counterparts + +- [dset](https://github.com/lukeed/dset) by [@lukeed](https://github.com/lukeed) is the spiritual "set" counterpart of `dlv` and very fast. +- [bury](https://github.com/kalmbach/bury) by [@kalmbach](https://github.com/kalmbach) does the opposite of `dlv` and is implemented in a very similar manner. + + +### License + +[MIT](https://oss.ninja/mit/developit/) + + +[preact]: https://github.com/developit/preact +[tests]: https://github.com/developit/dlv/blob/master/test.js + +----------- + +The following NPM package may be included in this product: + + - dom-helpers@5.2.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Jason Quense + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - earcut@2.2.4 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - esprima@4.0.1 + +This package contains the following license and notice below: + +Copyright JS Foundation and other contributors, https://js.foundation/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM packages may be included in this product: + + - fast-deep-equal@3.1.3 + - json-schema-traverse@0.4.1 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - fast-json-stable-stringify@2.1.0 + +This package contains the following license and notice below: + +This software is released under the MIT license: + +Copyright (c) 2017 Evgeny Poberezkin +Copyright (c) 2013 James Halliday + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - fastq@1.13.0 + +This package contains the following license and notice below: + +Copyright (c) 2015-2020, Matteo Collina + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - fill-range@7.0.1 + - is-number@7.0.0 + - micromatch@4.0.4 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - fs-extra@7.0.1 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2011-2017 JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - fsevents@2.3.2 + +This package contains the following license and notice below: + +MIT License +----------- + +Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - function-bind@1.1.1 + +This package contains the following license and notice below: + +Copyright (c) 2013 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - geojson-vt@3.2.1 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - get-stream@6.0.1 + - strip-json-comments@3.1.1 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - gl-matrix@3.4.3 + +This package contains the following license and notice below: + +Copyright (c) 2015-2021, Brandon Jones, Colin MacKenzie IV. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - glob-parent@5.1.2 + +This package contains the following license and notice below: + +The ISC License + +Copyright (c) 2015, 2019 Elan Shanker + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - glob-parent@6.0.2 + +This package contains the following license and notice below: + +The ISC License + +Copyright (c) 2015, 2019 Elan Shanker, 2021 Blaine Bublitz , Eric Schoffstall and other contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - graceful-fs@4.2.9 + +This package contains the following license and notice below: + +The ISC License + +Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - grid-index@1.1.0 + +This package contains the following license and notice below: + +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - has@1.0.3 + +This package contains the following license and notice below: + +Copyright (c) 2013 Thiago de Arruda + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - hashlru@2.3.0 + +This package contains the following license and notice below: + +Copyright (c) 2016 'Dominic Tarr' + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - ieee754@1.2.1 + +This package contains the following license and notice below: + +Copyright 2008 Fair Oaks Labs, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - immer@9.0.15 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2017 Michel Weststrate + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - import-lazy@4.0.0 + - quick-lru@5.1.1 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - invariant@2.2.4 + - prop-types@15.8.1 + - warning@4.0.3 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) 2013-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - is-core-module@2.9.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 Dave Justice + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - is-extglob@2.1.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-2016, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - is-glob@4.0.3 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - jju@1.4.0 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2013 Alex Kocharin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - js-levenshtein@1.1.6 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2017 Gustaf Andersson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - js-tokens@4.0.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - js-yaml@3.13.1 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (C) 2011-2015 by Vitaly Puzrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - jsonfile@4.0.0 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2012-2015, JP Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - kdbush@3.0.0 + - quickselect@2.0.0 + +These packages each contain the following license and notice below: + +ISC License + +Copyright (c) 2018, Vladimir Agafonkin + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - lilconfig@2.0.6 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2022 Anton Kastritskiy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - lodash.get@4.4.2 + +This package contains the following license and notice below: + +Copyright jQuery Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + +----------- + +The following NPM package may be included in this product: + + - lodash.isequal@4.5.0 + +This package contains the following license and notice below: + +Copyright JS Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + +----------- + +The following NPM package may be included in this product: + + - lodash@4.17.21 + +This package contains the following license and notice below: + +Copyright OpenJS Foundation and other contributors + +Based on Underscore.js, copyright Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/lodash/lodash + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules and vendor directories are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + +----------- + +The following NPM package may be included in this product: + + - loose-envify@1.4.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Andres Suarez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - lru-cache@6.0.0 + - semver@7.3.5 + - yallist@4.0.0 + +These packages each contain the following license and notice below: + +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - mapbox-gl@2.10.0 + +This package contains the following license and notice below: + +mapbox-gl-js v2.0 + +Mapbox Web SDK + +Copyright (c) 2020, Mapbox + +All rights reserved. + +Mapbox gl-js version 2.0 or higher (“Mapbox Web SDK”) must be used according to +the Mapbox Terms of Service. This license allows developers with a current active +Mapbox account to use and modify the Mapbox Web SDK. Developers may modify the +Mapbox Web SDK code so long as the modifications do not change or interfere with +marked portions of the code related to billing, accounting, and anonymized data +collection. The Mapbox Web SDK only sends anonymized usage data, which Mapbox uses +for fixing bugs and errors, accounting, and generating aggregated anonymized +statistics. This license terminates automatically if a user no longer has an +active Mapbox account. + +For the full license terms, please see the Mapbox Terms of Service at +https://www.mapbox.com/legal/tos/. + +------------------------------------------------------------------------------- + +Contains code from mapbox-gl-js v1.13 and earlier + +Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license + +Copyright (c) 2020, Mapbox +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Mapbox GL JS nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------------------- + +Contains a portion of d3-color https://github.com/d3/d3-color +Contains a portion of d3-geo https://github.com/d3/d3-geo +Contains a portion of d3-geo-projection https://github.com/d3/d3-geo-projection + +Copyright 2010-2021 Mike Bostock +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the author nor the names of contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- + +Contains code from glfx.js + +Copyright (C) 2011 by Evan Wallace + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - merge2@1.4.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014-2020 Teambition + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - mini-svg-data-uri@1.4.4 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2018 Taylor Hunt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - murmurhash-js@1.0.0 + +This package contains the following license and notice below: + +# MurmurHash.js + +An optimized JavaScript implementation of the MurmurHash algorithms. + +These algorithms take a JavaScript string (and a seed), and quickly create a non-cryptographic 32-bit hash from it. And by quick I mean sub-millisecond performance. + +More information about these algorithms can be found at: + +* [MurmurHash Homepage](http://sites.google.com/site/murmurhash/) +* [Wikipedia Entry on MurmurHash](http://en.wikipedia.org/wiki/MurmurHash) + +## Install + + npm install murmurhash-js + +## API + +```javascript +var murmur = require("murmurhash-js") +``` + +### Methods + +#### `murmur.murmur2(key, seed)` +Runs the murmur2 hash algorithm on the string `key` with initial seed `seed`. + +#### `murmur.murmur3(key, seed)` +Runs the murmur3 hash algorithm on the string `key` with initial seed `seed`. + +**Note** `require("murmur-hash")` is the same as `murmur.murmur3` + +## License (MIT) + +Copyright (c) 2011 Gary Court + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - nanoid@3.3.4 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2017 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - node-fetch@2.6.7 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2016 David Frank + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - object-assign@4.1.1 + - pify@2.3.0 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - object-hash@3.0.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 object-hash contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - path-parse@1.0.7 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Javier Blanco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - pbf@3.2.1 + +This package contains the following license and notice below: + +Copyright (c) 2017, Mapbox +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of pbf nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - picocolors@1.0.0 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2021 Alexey Raspopov, Kostiantyn Denysov, Anton Verinov + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - picomatch@2.3.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2017-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-import@14.1.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 Maxime Thirouin, Jason Campbell & Kevin Mårtensson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-js@4.0.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2015 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-load-config@3.1.4 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright Michael Ciniawsky + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-nested@5.0.6 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2014 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-selector-parser@6.0.10 + +This package contains the following license and notice below: + +Copyright (c) Ben Briggs (http://beneb.info) + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss-value-parser@4.2.0 + +This package contains the following license and notice below: + +Copyright (c) Bogdan Chadkin + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - postcss@8.4.16 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2013 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - potpack@1.0.2 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2018, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - protocol-buffers-schema@3.6.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - queue-microtask@1.2.3 + - run-parallel@1.2.0 + +These packages each contain the following license and notice below: + +The MIT License (MIT) + +Copyright (c) Feross Aboukhadijeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - react-collapsed@3.6.0 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2019-2020 Rogin Farrer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - react-dom@17.0.2 + - react-is@16.13.1 + - react@17.0.2 + - scheduler@0.20.2 + - use-sync-external-store@1.2.0 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - react-lifecycles-compat@3.0.4 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2013-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - read-cache@1.0.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2016 Bogdan Chadkin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - readdirp@3.6.0 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - recent-searches@1.0.5 + +This package contains the following license and notice below: + +

+ recent-searches 🕵️‍♀️🕵️‍♂️ +
+
+

+

A zero dependency JavaScript module that helps anyone build recent searches functionality into their search bar. +

+ + +
+ +[![Build Status][build-badge]][build] +[![downloads][downloads-badge]][npmcharts] [![version][version-badge]][package] +[![MIT License][license-badge]][license] +[![PRs Welcome][prs-badge]][prs] +[![size][size-badge]][unpkg-dist] [![gzip size][gzip-badge]][unpkg-dist] + +![Example implementation](https://raw.githubusercontent.com/JonasBa/recent-searches/master/RecentSearchesUX.gif) + +## The problem + +Building recent searches experience can be trickier than you think (expiry of queries, ranking of recent queries, handling storage etc...) + +## Solution +recent-searches module helps you build that experience without having to focus on the edge cases and technical details. If available, uses [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store suggestions cross sessions and in the rare cases where it might not be available uses a fallback memory storage, thus loosing it's cross session functionality. + +__The module handles:__ +- Searching and retrieving recent searches +- Ranking of searches and decaying their popularity (with different ranking options) +- Saving and expiring of searches through LocalStorage or MemoryStorage +- LocalStorage feature detection (with fallback to MemoryStorage) +- Safe LocalStorage usage (feature detection, limiting storage) + +## Table of Contents + + + + +- [Installation](#installation) +- [Usage](#usage) +- [LICENSE](#license) + + + +## Installation + +recent-searches is published on npm's public registry, you can install it as a dependancy of your project with the following command. +``` +npm install --save recent-searches +``` + +## Usage + +> [Standalone codesandbox example](https://codesandbox.io/s/8k21924m5l)
+> [Algolia react-instantsearch codesandbox example](https://codesandbox.io/s/m18wjy69)
+> [Algolia InstantSearch.js codesandbox example](https://codesandbox.io/s/62j3k7097r) +Initializing the module + +```ts +import RecentSearches from 'recent-searches' + +const searches = new RecentSearches({ + ttl: number, // Optional: ttl of searches in milliseconds, default to 24h (1000 * 60 * 60 * 24) + limit: number, // Optional: max number of entries that will be persisted, default is 50 + namespace: string, // Optional: custom localStorage namespace + ranking: string // Optional: ranking strategy of recent searches, "PROXIMITY" | "TIME" | "PROXIMITY_AND_TIME", default is "PROXIMITY_AND_TIME" +}) + +``` + +Setting and retrieving relevant searches. + +```ts +// Retrieve searches for a given query +const previousSearchesForJohn = searches.getRecentSearches("John") +/* + [ + {query: "John", data: {...}, timestamp: ...}, + {query: "Marc John", data: {...}, timestamp: ...} + ] +*/ + +// To set a recent search +searches.setRecentSearch("John", resultData) + +``` + +If you built something using recent-searches that you want to show, feel free to send us a link and we'll include it to the documentation! + +## Contributing + +This project is open to contributions, if you have a question, proposal or feedback please open a pull request or issue, we only ask you to be kind and respectful :) + +Special thanks to [Kent C. Dodds](https://twitter.com/kentcdodds?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor) for building downshift (making that demo was much easier) + +## LICENSE + +MIT + +[npm]: https://www.npmjs.com/ +[node]: https://nodejs.org +[build-badge]: https://circleci.com/gh/JonasBa/recent-searches/tree/master.svg?style=svg +[build]: https://circleci.com/gh/JonasBa/recent-searches +[coverage-badge]: https://img.shields.io/codecov/c/github/recent-searches/recent-searches.svg?style=flat-square +[coverage]: https://codecov.io/github/recent-searches/recent-searches +[version-badge]: https://img.shields.io/npm/v/recent-searches.svg?style=flat-square +[package]: https://www.npmjs.com/package/recent-searches +[downloads-badge]: https://img.shields.io/npm/dm/recent-searches.svg?style=flat-square +[npmcharts]: http://npmcharts.com/compare/recent-searches +[license-badge]: https://img.shields.io/npm/l/recent-searches.svg?style=flat-square +[license]: https://github.com/recent-searches/recent-searches/blob/master/LICENSE +[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square +[prs]: http://makeapullrequest.com +[react-badge]: https://img.shields.io/badge/%E2%9A%9B%EF%B8%8F-(p)react-00d8ff.svg?style=flat-square +[react]: https://facebook.github.io/react/ +[gzip-badge]: http://img.badgesize.io/https://unpkg.com/recent-searches/dist/index.min.js?compression=gzip&label=gzip%20size&style=flat-square +[size-badge]: http://img.badgesize.io/https://unpkg.com/recent-searches/dist/index.min.js?label=size&style=flat-square +[unpkg-dist]: https://unpkg.com/recent-searches/dist/ +[module-formats-badge]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20es-green.svg?style=flat-square +[spectrum-badge]: https://withspectrum.github.io/badge/badge.svg +[spectrum]: https://spectrum.chat/recent-searches +[emojis]: https://github.com/kentcdodds/all-contributors#emoji-key +[all-contributors]: https://github.com/kentcdodds/all-contributors +[ryan]: https://github.com/ryanflorence +[compound-components-lecture]: https://courses.reacttraining.com/courses/advanced-react/lectures/3060560 +[react-autocomplete]: https://www.npmjs.com/package/react-autocomplete +[jquery-complete]: https://jqueryui.com/autocomplete/ +[examples]: https://codesandbox.io/search?refinementList%5Btags%5D%5B0%5D=recent-searches%3Aexample&page=1 +[yt-playlist]: https://www.youtube.com/playlist?list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE + +----------- + +The following NPM package may be included in this product: + + - redux-thunk@2.4.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015-present Dan Abramov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - redux@4.2.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015-present Dan Abramov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - regenerator-runtime@0.13.9 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2014-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - reselect@4.1.6 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015-2018 Reselect Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - resolve-protobuf-schema@2.1.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 Mathias Buus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM packages may be included in this product: + + - resolve@1.17.0 + - resolve@1.19.0 + - resolve@1.22.1 + +These packages each contain the following license and notice below: + +MIT License + +Copyright (c) 2012 James Halliday + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - reusify@1.0.4 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Matteo Collina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - rw@1.3.3 + +This package contains the following license and notice below: + +Copyright (c) 2014-2016, Michael Bostock +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* The name Michael Bostock may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM packages may be included in this product: + + - source-map-js@1.0.2 + - source-map@0.6.1 + +These packages each contain the following license and notice below: + +Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the names of the Mozilla Foundation nor the names of project + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - sprintf-js@1.0.3 + +This package contains the following license and notice below: + +Copyright (c) 2007-2014, Alexandru Marasteanu +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of this software nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - string-argv@0.3.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright 2014 Anthony McCormick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - supercluster@7.1.5 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2021, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - supports-preserve-symlinks-flag@1.0.0 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2022 Inspect JS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - tailwind-merge@1.3.0 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2021 Dany Castillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - timsort@0.3.0 + +This package contains the following license and notice below: + +The MIT License + +Copyright (c) 2015 Marco Ziccardi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - tiny-warning@1.0.3 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) 2019 Alexander Reardon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - tinyqueue@2.0.3 + +This package contains the following license and notice below: + +ISC License + +Copyright (c) 2017, Vladimir Agafonkin + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - to-regex-range@5.0.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015-present, Jon Schlinkert. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - tr46@0.0.3 + +This package contains the following license and notice below: + +(MIT) + +----------- + +The following NPM package may be included in this product: + + - typescript@4.5.5 + +This package contains the following license and notice below: + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +----------- + +The following NPM package may be included in this product: + + - uncontrollable@7.2.1 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Jason Quense + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - universalify@0.1.2 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2017, Ryan Zimmerman + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the 'Software'), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - uri-js@4.4.1 + +This package contains the following license and notice below: + +Copyright 2011 Gary Court. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY GARY COURT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Gary Court. + +----------- + +The following NPM package may be included in this product: + + - use-isomorphic-layout-effect@1.1.2 + +This package contains the following license and notice below: + +MIT License + +Copyright (c) Mateusz Burzyński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - util-deprecate@1.0.2 + +This package contains the following license and notice below: + +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - validator@13.7.0 + +This package contains the following license and notice below: + +Copyright (c) 2018 Chris O'Hara + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - vt-pbf@3.1.3 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015 Anand Thakker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- + +Contains geojson_wrapper.js from https://github.com/mapbox/mapbox-gl-js + +Copyright (c) 2014, Mapbox + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Mapbox GL JS nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - webidl-conversions@3.0.1 + +This package contains the following license and notice below: + +# The BSD 2-Clause License + +Copyright (c) 2014, Domenic Denicola +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------- + +The following NPM package may be included in this product: + + - whatwg-url@5.0.0 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2015–2016 Sebastian Mayr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - xtend@4.0.2 + +This package contains the following license and notice below: + +The MIT License (MIT) +Copyright (c) 2012-2014 Raynos. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - yaml@1.10.2 + +This package contains the following license and notice below: + +Copyright 2018 Eemeli Aro + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +----------- + +The following NPM package may be included in this product: + + - z-schema@5.0.2 + +This package contains the following license and notice below: + +The MIT License (MIT) + +Copyright (c) 2014 Martin Zagora and other contributors +https://github.com/zaggino/z-schema/graphs/contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------- + +This file was generated with generate-license-file! https://www.npmjs.com/package/generate-license-file From ffc1ffbe84cd38a56f60e8da434bf8d969a25666 Mon Sep 17 00:00:00 2001 From: Connor Anderson Date: Wed, 28 Jun 2023 21:22:33 -0400 Subject: [PATCH 22/23] fix test site package-lock --- test-site/package-lock.json | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/test-site/package-lock.json b/test-site/package-lock.json index 09736301b..951d6ceaf 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,11 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", -<<<<<<< HEAD - "version": "1.2.0", -======= "version": "1.3.0-beta.2", ->>>>>>> develop "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -49,7 +45,7 @@ "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", "prop-types": "^15.8.1", - "react-collapsed": "~3.6.0", + "react-collapsed": "3.6.0", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", "use-isomorphic-layout-effect": "^1.1.2" @@ -84,11 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", -<<<<<<< HEAD - "@yext/search-headless-react": "^2.2.0", -======= "@yext/search-headless-react": "2.3.0-beta.0", ->>>>>>> develop "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -105,13 +97,9 @@ "typescript": "~4.5.5" }, "peerDependencies": { -<<<<<<< HEAD - "@yext/search-headless-react": "^2.2.0", -======= "@yext/search-headless-react": "2.3.0-beta.0", ->>>>>>> develop - "react": "^16.14 || ^17 || ^18", - "react-dom": "^16.14 || ^17 || ^18" + "react": "^16.14 || ^17", + "react-dom": "^16.14 || ^17" } }, "../node_modules/eslint-config-react-app": { @@ -20124,11 +20112,7 @@ "@typescript-eslint/parser": "^5.16.0", "@yext/analytics": "^0.2.0-beta.3", "@yext/eslint-config-slapshot": "^0.5.0", -<<<<<<< HEAD - "@yext/search-headless-react": "^2.2.0", -======= "@yext/search-headless-react": "2.3.0-beta.0", ->>>>>>> develop "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "classnames": "^2.3.1", @@ -20144,7 +20128,7 @@ "msw": "^0.36.8", "prop-types": "^15.8.1", "react": "^17.0.2", - "react-collapsed": "~3.6.0", + "react-collapsed": "3.6.0", "react-dom": "^17.0.2", "recent-searches": "^1.0.5", "tailwind-merge": "^1.3.0", From 22bc73f3e6f828074720cb6b92489bdb0b1b79bc Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Thu, 29 Jun 2023 09:01:30 -0400 Subject: [PATCH 23/23] Prepare version 1.3.0 (#380) Update the package version and the versions of the analytics lib and the search-headless-react library J=BACK-2362 TEST=manual Ran the test site locally and configured the analytics for sandbox and the EU. Confirmed the events were fired correctly --- THIRD-PARTY-NOTICES | 8 ++--- package-lock.json | 68 +++++++++++++++++++------------------ package.json | 8 ++--- test-site/package-lock.json | 12 +++---- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES index dcdb417f9..934140ddd 100644 --- a/THIRD-PARTY-NOTICES +++ b/THIRD-PARTY-NOTICES @@ -979,7 +979,7 @@ These definitions were written by Chi Vinh Le . The following NPM package may be included in this product: - - @yext/analytics@0.2.0-beta.3 + - @yext/analytics@0.5.0 This package contains the following license and notice below: @@ -1020,7 +1020,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-core@2.4.0-beta.0 + - @yext/search-core@2.4.0 This package contains the following license and notice below: @@ -1064,7 +1064,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless-react@2.3.0-beta.0 + - @yext/search-headless-react@2.3.0 This package contains the following license and notice below: @@ -1108,7 +1108,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The following NPM package may be included in this product: - - @yext/search-headless@2.4.0-beta.0 + - @yext/search-headless@2.4.0 This package contains the following license and notice below: diff --git a/package-lock.json b/package-lock.json index a9e689d5c..a89d23b17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.2", + "version": "1.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.2", + "version": "1.3.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -14,7 +14,7 @@ "@reach/auto-id": "^0.18.0", "@restart/ui": "^1.0.1", "@tailwindcss/forms": "^0.5.0", - "@yext/analytics": "^0.2.0-beta.3", + "@yext/analytics": "^0.5.0", "classnames": "^2.3.1", "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", @@ -54,7 +54,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -71,7 +71,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "react": "^16.14 || ^17", "react-dom": "^16.14 || ^17" } @@ -10700,10 +10700,11 @@ "dev": true }, "node_modules/@yext/analytics": { - "version": "0.2.0-beta.3", - "integrity": "sha512-bHhxVoMwEAsodg7n7W0dB6oTiWY/KnGL20XB2u9tw2FU9Fn/xlJV8aln1JakhiSatNqXNIHdC38PabqCWFuOrg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@yext/analytics/-/analytics-0.5.0.tgz", + "integrity": "sha512-KfM8Lh9U/1sm0tr+JL12hpnZdPuL09L14dYoAVH5FEd4U8TE4e6SZI6gMJ5BbjuL27EiqzsqD3QO04B+W8BO+w==", "dependencies": { - "cross-fetch": "^3.0.0" + "cross-fetch": "^3.1.5" } }, "node_modules/@yext/eslint-config-slapshot": { @@ -10738,9 +10739,9 @@ } }, "node_modules/@yext/search-core": { - "version": "2.4.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0-beta.0.tgz", - "integrity": "sha512-daes8WoldwfPmCVxiiT/WZMaBBF7S3ax3pzVLc+z3nmnRDDQBLY1TrnYQqsasfb1YKpy4Qv/+6y/d2pR4IdYgA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0.tgz", + "integrity": "sha512-slPiKO3lENIB8aqr509ljjkaHJ9UJXnBu0iSv8CAdFsoZomdXTQExlbGBPHVZ6q0rEBahDR4PSMWlvFp9b7yAQ==", "dev": true, "dependencies": { "@babel/runtime-corejs3": "^7.12.5", @@ -10751,24 +10752,24 @@ } }, "node_modules/@yext/search-headless": { - "version": "2.4.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0-beta.0.tgz", - "integrity": "sha512-m5+2chjp3hm80pMpNLMBSqECW4B0WmQg+gQdLVvYZRm9EjQeyR2G9gtU6IyjwcmOL+JY2sMoG5Z2yxUPJtHCPw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0.tgz", + "integrity": "sha512-N4++EBuV9KWiS6sX29g/EeXUvprycx3QXS3OHZLYVy4n0Y4xNsItXAeb4qZywj6AN8Wi+lEPgUsWQA00WzxBcw==", "dev": true, "dependencies": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "2.4.0-beta.0", + "@yext/search-core": "^2.4.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "node_modules/@yext/search-headless-react": { - "version": "2.3.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0-beta.0.tgz", - "integrity": "sha512-hFcWOvcyEYSvBO5YHCFqory/j/Pm/geDg0ENH29J9i3uMa1i5sTCWpUhh5HjOpJBPt3rLmmLKa1KTMQf5t9sTA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0.tgz", + "integrity": "sha512-E3b+o90FwVrBc/84UYS8cClirsTU+9U8sa4UBMAnamPYPhge3DwNWQeA/hfQhTU6dGoaUnEiz4uTkSh8bhY3Rg==", "dev": true, "dependencies": { - "@yext/search-headless": "2.4.0-beta.0", + "@yext/search-headless": "^2.4.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { @@ -41079,10 +41080,11 @@ "dev": true }, "@yext/analytics": { - "version": "0.2.0-beta.3", - "integrity": "sha512-bHhxVoMwEAsodg7n7W0dB6oTiWY/KnGL20XB2u9tw2FU9Fn/xlJV8aln1JakhiSatNqXNIHdC38PabqCWFuOrg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@yext/analytics/-/analytics-0.5.0.tgz", + "integrity": "sha512-KfM8Lh9U/1sm0tr+JL12hpnZdPuL09L14dYoAVH5FEd4U8TE4e6SZI6gMJ5BbjuL27EiqzsqD3QO04B+W8BO+w==", "requires": { - "cross-fetch": "^3.0.0" + "cross-fetch": "^3.1.5" } }, "@yext/eslint-config-slapshot": { @@ -41108,9 +41110,9 @@ } }, "@yext/search-core": { - "version": "2.4.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0-beta.0.tgz", - "integrity": "sha512-daes8WoldwfPmCVxiiT/WZMaBBF7S3ax3pzVLc+z3nmnRDDQBLY1TrnYQqsasfb1YKpy4Qv/+6y/d2pR4IdYgA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@yext/search-core/-/search-core-2.4.0.tgz", + "integrity": "sha512-slPiKO3lENIB8aqr509ljjkaHJ9UJXnBu0iSv8CAdFsoZomdXTQExlbGBPHVZ6q0rEBahDR4PSMWlvFp9b7yAQ==", "dev": true, "requires": { "@babel/runtime-corejs3": "^7.12.5", @@ -41118,24 +41120,24 @@ } }, "@yext/search-headless": { - "version": "2.4.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0-beta.0.tgz", - "integrity": "sha512-m5+2chjp3hm80pMpNLMBSqECW4B0WmQg+gQdLVvYZRm9EjQeyR2G9gtU6IyjwcmOL+JY2sMoG5Z2yxUPJtHCPw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless/-/search-headless-2.4.0.tgz", + "integrity": "sha512-N4++EBuV9KWiS6sX29g/EeXUvprycx3QXS3OHZLYVy4n0Y4xNsItXAeb4qZywj6AN8Wi+lEPgUsWQA00WzxBcw==", "dev": true, "requires": { "@reduxjs/toolkit": "^1.8.1", - "@yext/search-core": "2.4.0-beta.0", + "@yext/search-core": "^2.4.0", "js-levenshtein": "^1.1.6", "lodash": "^4.17.21" } }, "@yext/search-headless-react": { - "version": "2.3.0-beta.0", - "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0-beta.0.tgz", - "integrity": "sha512-hFcWOvcyEYSvBO5YHCFqory/j/Pm/geDg0ENH29J9i3uMa1i5sTCWpUhh5HjOpJBPt3rLmmLKa1KTMQf5t9sTA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yext/search-headless-react/-/search-headless-react-2.3.0.tgz", + "integrity": "sha512-E3b+o90FwVrBc/84UYS8cClirsTU+9U8sa4UBMAnamPYPhge3DwNWQeA/hfQhTU6dGoaUnEiz4uTkSh8bhY3Rg==", "dev": true, "requires": { - "@yext/search-headless": "2.4.0-beta.0", + "@yext/search-headless": "^2.4.0", "use-sync-external-store": "^1.1.0" } }, diff --git a/package.json b/package.json index 7d284e1e0..c138651e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.2", + "version": "1.3.0", "description": "A library of React Components for powering Yext Search integrations", "author": "slapshot@yext.com", "license": "BSD-3-Clause", @@ -76,7 +76,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -93,7 +93,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "react": "^16.14 || ^17", "react-dom": "^16.14 || ^17" }, @@ -141,7 +141,7 @@ "@reach/auto-id": "^0.18.0", "@restart/ui": "^1.0.1", "@tailwindcss/forms": "^0.5.0", - "@yext/analytics": "^0.2.0-beta.3", + "@yext/analytics": "^0.5.0", "classnames": "^2.3.1", "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", diff --git a/test-site/package-lock.json b/test-site/package-lock.json index 951d6ceaf..a7d5d9940 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -32,7 +32,7 @@ }, "..": { "name": "@yext/search-ui-react", - "version": "1.3.0-beta.2", + "version": "1.3.0", "license": "BSD-3-Clause", "dependencies": { "@microsoft/api-documenter": "^7.15.3", @@ -40,7 +40,7 @@ "@reach/auto-id": "^0.18.0", "@restart/ui": "^1.0.1", "@tailwindcss/forms": "^0.5.0", - "@yext/analytics": "^0.2.0-beta.3", + "@yext/analytics": "^0.5.0", "classnames": "^2.3.1", "lodash": "^4.17.21", "mapbox-gl": "^2.9.2", @@ -80,7 +80,7 @@ "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "eslint": "^8.11.0", @@ -97,7 +97,7 @@ "typescript": "~4.5.5" }, "peerDependencies": { - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "react": "^16.14 || ^17", "react-dom": "^16.14 || ^17" } @@ -20110,9 +20110,9 @@ "@types/react": "^17.0.38", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", - "@yext/analytics": "^0.2.0-beta.3", + "@yext/analytics": "^0.5.0", "@yext/eslint-config-slapshot": "^0.5.0", - "@yext/search-headless-react": "2.3.0-beta.0", + "@yext/search-headless-react": "2.3.0", "axe-playwright": "^1.1.11", "babel-jest": "^27.0.6", "classnames": "^2.3.1",